Compare commits

..

No commits in common. "master" and "3.9.4" have entirely different histories.

583 changed files with 7518 additions and 13927 deletions

View file

@ -1,8 +1,5 @@
name: build name: build
env:
COSMOCC_VERSION: 3.9.2
on: on:
push: push:
branches: branches:
@ -22,48 +19,13 @@ jobs:
matrix: matrix:
mode: ["", tiny, rel, tinylinux, optlinux] mode: ["", tiny, rel, tinylinux, optlinux]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
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 - name: support ape bins 1
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
- name: support ape bins 2 - name: support ape bins 2
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
- name: make matrix - name: make matrix
run: V=0 make -j2 MODE=${{ matrix.mode }} 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 }}

1
.gitignore vendored
View file

@ -15,4 +15,3 @@ __pycache__
/tool/emacs/*.elc /tool/emacs/*.elc
/perf.data /perf.data
/perf.data.old /perf.data.old
/qemu*core

View file

@ -77,8 +77,7 @@ COMMA := ,
PWD := $(shell pwd) PWD := $(shell pwd)
# detect wsl2 running cosmopolitan binaries on the host by checking whether: # detect wsl2 running cosmopolitan binaries on the host by checking whether:
# - user ran .cosmocc/current/bin/make, in which case make's working directory # - user ran build/bootstrap/make, in which case make's working directory is in wsl
# is in wsl
# - user ran make, in which case cocmd'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)),) ifneq ($(findstring //wsl.localhost/,$(CURDIR) $(PWD)),)
$(warning wsl2 interop is enabled) $(warning wsl2 interop is enabled)
@ -90,7 +89,7 @@ UNAME_S := $(shell uname -s)
# apple still distributes a 17 year old version of gnu make # apple still distributes a 17 year old version of gnu make
ifeq ($(MAKE_VERSION), 3.81) ifeq ($(MAKE_VERSION), 3.81)
$(error please use https://cosmo.zip/pub/cosmos/bin/make) $(error please use build/bootstrap/make)
endif endif
LC_ALL = C LC_ALL = C
@ -136,7 +135,7 @@ ARCH = aarch64
HOSTS ?= pi pi5 studio freebsdarm HOSTS ?= pi pi5 studio freebsdarm
else else
ARCH = x86_64 ARCH = x86_64
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10 luna HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10
endif endif
ZIPOBJ_FLAGS += -a$(ARCH) ZIPOBJ_FLAGS += -a$(ARCH)
@ -275,10 +274,6 @@ include libc/BUILD.mk #─┘
include libc/sock/BUILD.mk #─┐ include libc/sock/BUILD.mk #─┐
include net/http/BUILD.mk # ├──ONLINE RUNTIME include net/http/BUILD.mk # ├──ONLINE RUNTIME
include third_party/musl/BUILD.mk # │ You can communicate with the network 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 libc/x/BUILD.mk # │
include dsp/scale/BUILD.mk # │ include dsp/scale/BUILD.mk # │
include dsp/mpeg/BUILD.mk # │ include dsp/mpeg/BUILD.mk # │
@ -298,7 +293,8 @@ include third_party/libcxx/BUILD.mk # │
include third_party/openmp/BUILD.mk # │ include third_party/openmp/BUILD.mk # │
include third_party/pcre/BUILD.mk # │ include third_party/pcre/BUILD.mk # │
include third_party/less/BUILD.mk # │ include third_party/less/BUILD.mk # │
include net/https/BUILD.mk #─┘ include net/https/BUILD.mk # │
include third_party/regex/BUILD.mk #─┘
include third_party/tidy/BUILD.mk include third_party/tidy/BUILD.mk
include third_party/BUILD.mk include third_party/BUILD.mk
include third_party/nsync/testing/BUILD.mk include third_party/nsync/testing/BUILD.mk
@ -317,6 +313,8 @@ include third_party/double-conversion/test/BUILD.mk
include third_party/lua/BUILD.mk include third_party/lua/BUILD.mk
include third_party/tree/BUILD.mk include third_party/tree/BUILD.mk
include third_party/zstd/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/awk/BUILD.mk
include third_party/hiredis/BUILD.mk include third_party/hiredis/BUILD.mk
include third_party/make/BUILD.mk include third_party/make/BUILD.mk
@ -369,7 +367,6 @@ include test/libc/fmt/BUILD.mk
include test/libc/time/BUILD.mk include test/libc/time/BUILD.mk
include test/libc/proc/BUILD.mk include test/libc/proc/BUILD.mk
include test/libc/stdio/BUILD.mk include test/libc/stdio/BUILD.mk
include test/libc/system/BUILD.mk
include test/libc/BUILD.mk include test/libc/BUILD.mk
include test/net/http/BUILD.mk include test/net/http/BUILD.mk
include test/net/https/BUILD.mk include test/net/https/BUILD.mk
@ -459,7 +456,6 @@ COSMOPOLITAN = \
LIBC_NT_POWRPROF \ LIBC_NT_POWRPROF \
LIBC_NT_PSAPI \ LIBC_NT_PSAPI \
LIBC_NT_REALTIME \ LIBC_NT_REALTIME \
LIBC_NT_SHELL32 \
LIBC_NT_SYNCHRONIZATION \ LIBC_NT_SYNCHRONIZATION \
LIBC_NT_USER32 \ LIBC_NT_USER32 \
LIBC_NT_WS2_32 \ LIBC_NT_WS2_32 \
@ -468,7 +464,6 @@ COSMOPOLITAN = \
LIBC_SOCK \ LIBC_SOCK \
LIBC_STDIO \ LIBC_STDIO \
LIBC_STR \ LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \ LIBC_SYSV \
LIBC_SYSV_CALLS \ LIBC_SYSV_CALLS \
LIBC_THREAD \ LIBC_THREAD \

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) [![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
# Cosmopolitan # Cosmopolitan
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++ [Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
a build-once run-anywhere language, like Java, except it doesn't need an a build-once run-anywhere language, like Java, except it doesn't need an
interpreter or virtual machine. Instead, it reconfigures stock GCC and interpreter or virtual machine. Instead, it reconfigures stock GCC and
Clang to output a POSIX-approved polyglot format that runs natively on Clang to output a POSIX-approved polyglot format that runs natively on
Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS with the best
best possible performance and the tiniest footprint imaginable. possible performance and the tiniest footprint imaginable.
## Background ## Background
@ -87,22 +87,15 @@ ape/apeinstall.sh
``` ```
You can now build the mono repo with any modern version of GNU Make. To You can now build the mono repo with any modern version of GNU Make. To
bootstrap your build, you can install Cosmopolitan Make from this site: make life easier, we've included one in the cosmocc toolchain, which is
guaranteed to be compatible and furthermore includes our extensions for
https://cosmo.zip/pub/cosmos/bin/make doing build system sandboxing.
E.g.:
```sh ```sh
curl -LO https://cosmo.zip/pub/cosmos/bin/make build/bootstrap/make -j8
./make -j8
o//examples/hello 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 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 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 compiled relatively quickly, which is a simple POSIX test that only
@ -110,7 +103,7 @@ depends on core LIBC packages.
```sh ```sh
rm -rf o//libc o//test rm -rf o//libc o//test
.cosmocc/current/bin/make o//test/posix/signal_test build/bootstrap/make o//test/posix/signal_test
o//test/posix/signal_test o//test/posix/signal_test
``` ```
@ -119,21 +112,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: all the unit tests in the `TEST_POSIX` package, you could say:
```sh ```sh
.cosmocc/current/bin/make o//test/posix build/bootstrap/make o//test/posix
``` ```
Cosmopolitan provides a variety of build modes. For example, if you want 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: really tiny binaries (as small as 12kb in size) then you'd say:
```sh ```sh
.cosmocc/current/bin/make m=tiny build/bootstrap/make m=tiny
``` ```
You can furthermore cut out the bloat of other operating systems, and You can furthermore cut out the bloat of other operating systems, and
have Cosmopolitan become much more similar to Musl Libc. have Cosmopolitan become much more similar to Musl Libc.
```sh ```sh
.cosmocc/current/bin/make m=tinylinux build/bootstrap/make m=tinylinux
``` ```
For further details, see [//build/config.mk](build/config.mk). For further details, see [//build/config.mk](build/config.mk).
@ -256,7 +249,7 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
| Linux | 2.6.18 | 2007 | | Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 | | Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 | | Darwin (macOS) | 23.1.0+ | 2023 |
| OpenBSD | 7.3 or earlier | 2023 | | OpenBSD | 7 | 2021 |
| FreeBSD | 13 | 2020 | | FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 | | NetBSD | 9.2 | 2021 |

View file

@ -259,9 +259,6 @@ SECTIONS {
.debug_ranges 0 : { *(.debug_ranges) } .debug_ranges 0 : { *(.debug_ranges) }
.debug_macro 0 : { *(.debug_macro) } .debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) } .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)) } .ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) } .note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) }

View file

@ -386,13 +386,6 @@ SECTIONS {
_tbss_end = .; _tbss_end = .;
} :Tls } :Tls
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
} :Ram
.data . : { .data . : {
/*BEGIN: Read/Write Data */ /*BEGIN: Read/Write Data */
#if SupportsWindows() #if SupportsWindows()
@ -433,6 +426,11 @@ SECTIONS {
KEEP(*(.dtors)) KEEP(*(.dtors))
__fini_array_end = .; __fini_array_end = .;
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
/*BEGIN: Post-Initialization Read-Only */ /*BEGIN: Post-Initialization Read-Only */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0); . = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*))) KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
@ -441,6 +439,7 @@ SECTIONS {
KEEP(*(.piro.pad.data)) KEEP(*(.piro.pad.data))
*(.igot.plt) *(.igot.plt)
KEEP(*(.dataepilogue)) KEEP(*(.dataepilogue))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0); . = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
/*END: NT FORK COPYING */ /*END: NT FORK COPYING */
_edata = .; _edata = .;
@ -520,9 +519,6 @@ SECTIONS {
.debug_rnglists 0 : { *(.debug_rnglists) } .debug_rnglists 0 : { *(.debug_rnglists) }
.debug_macro 0 : { *(.debug_macro) } .debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) } .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)) } .gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
.GCC.command.line 0 : { *(.GCC.command.line) } .GCC.command.line 0 : { *(.GCC.command.line) }
@ -586,11 +582,11 @@ ape_rom_memsz = ape_rom_filesz;
ape_rom_align = CONSTANT(COMMONPAGESIZE); ape_rom_align = CONSTANT(COMMONPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr); ape_rom_rva = RVA(ape_rom_vaddr);
ape_ram_vaddr = ADDR(.eh_frame); ape_ram_vaddr = ADDR(.data);
ape_ram_offset = ape_ram_vaddr - __executable_start; ape_ram_offset = ape_ram_vaddr - __executable_start;
ape_ram_paddr = LOADADDR(.eh_frame); ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame); ape_ram_filesz = ADDR(.bss) - ADDR(.data);
ape_ram_memsz = _end - ADDR(.eh_frame); ape_ram_memsz = _end - ADDR(.data);
ape_ram_align = CONSTANT(COMMONPAGESIZE); ape_ram_align = CONSTANT(COMMONPAGESIZE);
ape_ram_rva = RVA(ape_ram_vaddr); ape_ram_rva = RVA(ape_ram_vaddr);
@ -600,7 +596,7 @@ ape_stack_offset = 0;
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000; ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz; ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
ape_stack_filesz = 0; ape_stack_filesz = 0;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 4 * 1024 * 1024; ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024;
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr); ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
ape_note_filesz = ape_note_end - ape_note; ape_note_filesz = ape_note_end - ape_note;

View file

@ -10,8 +10,8 @@ if [ ! -f ape/loader.c ]; then
cd "$COSMO" || exit cd "$COSMO" || exit
fi fi
if [ -x .cosmocc/current/bin/make ]; then if [ -x build/bootstrap/make ]; then
MAKE=.cosmocc/current/bin/make MAKE=build/bootstrap/make
else else
MAKE=make MAKE=make
fi fi

View file

@ -99,8 +99,3 @@ rm -f cosmocc.zip cosmocc.zip.sha256sum
# commit output directory # commit output directory
cd "${OLDPWD}" || die cd "${OLDPWD}" || die
mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die
# update current symlink
BASE=$(basename "${OUTPUT_DIR}")
DIR=$(dirname "${OUTPUT_DIR}")
ln -sfn "$BASE" "$DIR/current"

View file

@ -97,12 +97,12 @@ class shared_ref
size_t use_count() const noexcept size_t use_count() const noexcept
{ {
return __atomic_load_n(&shared, __ATOMIC_RELAXED) + 1; return shared + 1;
} }
size_t weak_count() const noexcept size_t weak_count() const noexcept
{ {
return __atomic_load_n(&weak, __ATOMIC_RELAXED); return weak;
} }
private: private:
@ -349,7 +349,7 @@ class shared_ptr
template<typename U> template<typename U>
bool owner_before(const weak_ptr<U>& r) const noexcept bool owner_before(const weak_ptr<U>& r) const noexcept
{ {
return rc < r.rc; return !r.owner_before(*this);
} }
private: private:
@ -382,34 +382,6 @@ class weak_ptr
rc->keep_weak(); 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() ~weak_ptr()
{ {
if (rc) if (rc)
@ -438,19 +410,6 @@ class weak_ptr
swap(rc, r.rc); 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 shared_ptr<T> lock() const noexcept
{ {
if (expired()) if (expired())

View file

@ -383,72 +383,4 @@ string::erase(const size_t pos, size_t count) noexcept
return *this; 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 } // namespace ctl

View file

@ -125,7 +125,6 @@ class string
void append(char, size_t) noexcept; void append(char, size_t) noexcept;
void append(unsigned long) noexcept; void append(unsigned long) noexcept;
void append(const void*, size_t) 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& insert(size_t, ctl::string_view) noexcept;
string& erase(size_t = 0, size_t = npos) noexcept; string& erase(size_t = 0, size_t = npos) noexcept;
string substr(size_t = 0, size_t = npos) const noexcept; string substr(size_t = 0, size_t = npos) const noexcept;
@ -137,10 +136,6 @@ class string
bool starts_with(ctl::string_view) const noexcept; bool starts_with(ctl::string_view) const noexcept;
size_t find(char, size_t = 0) 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(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 void swap(string& s) noexcept
{ {
@ -307,7 +302,7 @@ class string
append(ch); append(ch);
} }
void append(const ctl::string_view& s) noexcept void append(const ctl::string_view s) noexcept
{ {
append(s.p, s.n); append(s.p, s.n);
} }

View file

@ -108,66 +108,4 @@ string_view::starts_with(const string_view s) const noexcept
return !memcmp(p, s.p, s.n); 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 } // namespace ctl

View file

@ -45,10 +45,6 @@ struct string_view
string_view substr(size_t = 0, size_t = npos) const noexcept; string_view substr(size_t = 0, size_t = npos) const noexcept;
size_t find(char, size_t = 0) const noexcept; size_t find(char, size_t = 0) const noexcept;
size_t find(string_view, 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 constexpr string_view& operator=(const string_view s) noexcept
{ {
@ -113,12 +109,12 @@ struct string_view
return p[n - 1]; return p[n - 1];
} }
constexpr const_iterator begin() const noexcept constexpr const_iterator begin() noexcept
{ {
return p; return p;
} }
constexpr const_iterator end() const noexcept constexpr const_iterator end() noexcept
{ {
return p + n; return p + n;
} }

View file

@ -54,8 +54,8 @@ EXAMPLES_DIRECTDEPS = \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \ LIBC_NT_ADVAPI32 \
LIBC_NT_IPHLPAPI \ LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_MEMORY \ LIBC_NT_MEMORY \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \ LIBC_NT_NTDLL \
LIBC_NT_USER32 \ LIBC_NT_USER32 \
LIBC_NT_WS2_32 \ LIBC_NT_WS2_32 \
@ -64,7 +64,6 @@ EXAMPLES_DIRECTDEPS = \
LIBC_SOCK \ LIBC_SOCK \
LIBC_STDIO \ LIBC_STDIO \
LIBC_STR \ LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \ LIBC_SYSV \
LIBC_SYSV_CALLS \ LIBC_SYSV_CALLS \
LIBC_TESTLIB \ LIBC_TESTLIB \
@ -82,8 +81,6 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_GETOPT \ THIRD_PARTY_GETOPT \
THIRD_PARTY_HIREDIS \ THIRD_PARTY_HIREDIS \
THIRD_PARTY_LIBCXX \ THIRD_PARTY_LIBCXX \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_LINENOISE \ THIRD_PARTY_LINENOISE \
THIRD_PARTY_LUA \ THIRD_PARTY_LUA \
THIRD_PARTY_MBEDTLS \ THIRD_PARTY_MBEDTLS \
@ -97,10 +94,12 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_TZ \ THIRD_PARTY_TZ \
THIRD_PARTY_VQSORT \ THIRD_PARTY_VQSORT \
THIRD_PARTY_XED \ THIRD_PARTY_XED \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_ZLIB \ THIRD_PARTY_ZLIB \
TOOL_ARGS \ TOOL_ARGS \
TOOL_BUILD_LIB \ TOOL_BUILD_LIB \
TOOL_VIZ_LIB \ TOOL_VIZ_LIB
EXAMPLES_DEPS := \ EXAMPLES_DEPS := \
$(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x)))) $(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x))))

View file

@ -1,353 +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 <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 <20> 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);
}

View file

@ -1,353 +0,0 @@
// -*- 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;
}

View file

@ -337,7 +337,7 @@ int main(int argc, char *argv[]) {
sigaddset(&block, SIGQUIT); sigaddset(&block, SIGQUIT);
pthread_attr_t attr; pthread_attr_t attr;
unassert(!pthread_attr_init(&attr)); unassert(!pthread_attr_init(&attr));
unassert(!pthread_attr_setstacksize(&attr, 65536 - getpagesize())); unassert(!pthread_attr_setstacksize(&attr, 65536));
unassert(!pthread_attr_setguardsize(&attr, getpagesize())); unassert(!pthread_attr_setguardsize(&attr, getpagesize()));
unassert(!pthread_attr_setsigmask_np(&attr, &block)); unassert(!pthread_attr_setsigmask_np(&attr, &block));
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0)); unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));

View file

@ -1,322 +0,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/ │
*/
#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");
}

View file

@ -49,10 +49,10 @@
* @fileoverview Terminal Screencast Recorder / Player, e.g. * @fileoverview Terminal Screencast Recorder / Player, e.g.
* *
* make o//examples/script.com * make o//examples/script.com
* o//examples/script.com -w80 -h24 -r recording.tty * o//examples/script.com -r
* # type stuff.. * # type stuff..
* # CTRL-D * # CTRL-D
* o//examples/script.com -p recording.tty * o//examples/script.com -p typescript
* *
* @note works on Linux, OpenBSD, NetBSD, FreeBSD, MacOS * @note works on Linux, OpenBSD, NetBSD, FreeBSD, MacOS
* @see https://asciinema.org/ * @see https://asciinema.org/
@ -103,9 +103,9 @@ main(int argc, char *argv[])
fd_set rfd; fd_set rfd;
int fm_fd; int fm_fd;
int aflg, Fflg, kflg, pflg, ch, k, n; int aflg, Fflg, kflg, pflg, ch, k, n;
int flushtime, readstdin, width, height; int flushtime, readstdin;
aflg = Fflg = kflg = pflg = height = width = 0; aflg = Fflg = kflg = pflg = 0;
usesleep = 1; usesleep = 1;
rawout = 0; rawout = 0;
flushtime = 30; flushtime = 30;
@ -115,7 +115,7 @@ main(int argc, char *argv[])
(void)fm_fd; (void)fm_fd;
while ((ch = getopt(argc, argv, "adeFfkpqrt:w:h:")) != -1) while ((ch = getopt(argc, argv, "adeFfkpqrt:")) != -1)
switch(ch) { switch(ch) {
case 'a': case 'a':
aflg = 1; aflg = 1;
@ -145,12 +145,6 @@ main(int argc, char *argv[])
if (flushtime < 0) if (flushtime < 0)
err(1, "invalid flush time %d", flushtime); err(1, "invalid flush time %d", flushtime);
break; break;
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case '?': case '?':
default: default:
usage(); usage();
@ -178,10 +172,6 @@ main(int argc, char *argv[])
if (openpty(&master, &slave, NULL, NULL, NULL) == -1) if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
err(1, "openpty"); err(1, "openpty");
} else { } else {
if (width)
win.ws_col = width;
if (height)
win.ws_row = height;
if (openpty(&master, &slave, NULL, &tt, &win) == -1) if (openpty(&master, &slave, NULL, &tt, &win) == -1)
err(1, "openpty"); err(1, "openpty");
ttyflg = 1; ttyflg = 1;

View file

@ -7,13 +7,9 @@
http://creativecommons.org/publicdomain/zero/1.0/ │ http://creativecommons.org/publicdomain/zero/1.0/ │
*/ */
#endif #endif
#include "libc/dce.h"
#include "libc/intrin/maps.h"
#include "libc/mem/alg.h" #include "libc/mem/alg.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/winargs.internal.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/x/xasprintf.h" #include "libc/x/xasprintf.h"
@ -71,18 +67,8 @@ int main(int argc, char *argv[]) {
Append((uintptr_t)&__auxv[i + 1], Append((uintptr_t)&__auxv[i + 1],
xasprintf("&auxv[%d] = %#lx", i + 1, __auxv[i + 1])); xasprintf("&auxv[%d] = %#lx", i + 1, __auxv[i + 1]));
} }
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); qsort(things.p, things.n, sizeof(*things.p), Compare);
for (int i = 0; i < things.n; ++i) for (int i = 0; i < things.n; ++i) {
printf("%012lx %s\n", things.p[i].i, things.p[i].s); printf("%012lx %s\n", things.p[i].i, things.p[i].s);
}
} }

View file

@ -1,17 +0,0 @@
#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);
}

View file

@ -216,6 +216,12 @@ o//libc/calls/writev.o: private \
-mgeneral-regs-only -mgeneral-regs-only
# these assembly files are safe to build on aarch64 # 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 o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<

View file

@ -59,7 +59,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § Windows Time // —Quoth MSDN § Windows Time
// //
QueryUnbiasedInterruptTimePrecise(&hectons); QueryUnbiasedInterruptTimePrecise(&hectons);
*ts = WindowsDurationToTimeSpec(hectons); *ts = timespec_fromnanos(hectons * 100);
return 0; return 0;
case _CLOCK_MONOTONIC_COARSE: case _CLOCK_MONOTONIC_COARSE:
// //
@ -83,7 +83,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § QueryUnbiasedInterruptTimePrecise // —Quoth MSDN § QueryUnbiasedInterruptTimePrecise
// //
QueryUnbiasedInterruptTime(&hectons); QueryUnbiasedInterruptTime(&hectons);
*ts = WindowsDurationToTimeSpec(hectons); *ts = timespec_fromnanos(hectons * 100);
return 0; return 0;
case _CLOCK_BOOTTIME: case _CLOCK_BOOTTIME:
// //
@ -95,7 +95,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § Interrupt Time // —Quoth MSDN § Interrupt Time
// //
QueryInterruptTimePrecise(&hectons); QueryInterruptTimePrecise(&hectons);
*ts = WindowsDurationToTimeSpec(hectons); *ts = timespec_fromnanos(hectons * 100);
return 0; return 0;
case _CLOCK_PROCESS_CPUTIME_ID: case _CLOCK_PROCESS_CPUTIME_ID:
GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel, GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel,

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/atomic.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
@ -24,37 +23,26 @@
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/ntdll.h"
#include "libc/stdio/sysparam.h" #include "libc/stdio/sysparam.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/timer.h" #include "libc/sysv/consts/timer.h"
#include "libc/thread/tls.h" #include "libc/thread/tls.h"
#ifdef __x86_64__ #ifdef __x86_64__
static atomic_int usingRes;
static atomic_bool changedRes;
static textwindows int sys_clock_nanosleep_nt_impl(int clock, static textwindows int sys_clock_nanosleep_nt_impl(int clock,
struct timespec abs, struct timespec abs,
sigset_t waitmask) { sigset_t waitmask) {
struct timespec now, wall; uint32_t msdelay;
uint32_t minRes, maxRes, oldRes; struct timespec now;
sys_clock_gettime_nt(0, &wall); for (;;) {
if (sys_clock_gettime_nt(clock, &now)) if (sys_clock_gettime_nt(clock, &now))
return -1; return -1;
bool wantRes = clock == CLOCK_REALTIME || // if (timespec_cmp(now, abs) >= 0)
clock == CLOCK_MONOTONIC || // return 0;
clock == CLOCK_BOOTTIME; msdelay = timespec_tomillis(timespec_sub(abs, now));
if (wantRes && !atomic_fetch_add(&usingRes, 1)) msdelay = MIN(msdelay, -1u);
changedRes = NtSuccess(NtQueryTimerResolution(&minRes, &maxRes, &oldRes)) && if (_park_norestart(msdelay, waitmask) == -1)
NtSuccess(NtSetTimerResolution(maxRes, true, &oldRes)); return -1;
if (timespec_cmp(abs, now) > 0) }
wall = timespec_add(wall, timespec_sub(abs, now));
int rc = _park_norestart(wall, waitmask);
if (wantRes && atomic_fetch_sub(&usingRes, 1) == 1 && changedRes)
NtSetTimerResolution(0, false, &minRes);
return rc;
} }
textwindows int sys_clock_nanosleep_nt(int clock, int flags, textwindows int sys_clock_nanosleep_nt(int clock, int flags,

View file

@ -23,7 +23,7 @@
#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
relegated int sys_clock_nanosleep_openbsd(int clock, int flags, int sys_clock_nanosleep_openbsd(int clock, int flags,
const struct timespec *req, const struct timespec *req,
struct timespec *rem) { struct timespec *rem) {
int res; int res;

View file

@ -57,7 +57,6 @@
* *
* @param clock may be * @param clock may be
* - `CLOCK_REALTIME` * - `CLOCK_REALTIME`
* - `CLOCK_BOOTTIME`
* - `CLOCK_MONOTONIC` * - `CLOCK_MONOTONIC`
* - `CLOCK_REALTIME_COARSE` but is likely to sleep negative time * - `CLOCK_REALTIME_COARSE` but is likely to sleep negative time
* - `CLOCK_MONTONIC_COARSE` but is likely to sleep negative time * - `CLOCK_MONTONIC_COARSE` but is likely to sleep negative time

View file

@ -51,7 +51,6 @@
#include "libc/sysv/consts/fio.h" #include "libc/sysv/consts/fio.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
struct FileLock { struct FileLock {
@ -68,9 +67,7 @@ struct FileLocks {
struct FileLock *free; struct FileLock *free;
}; };
static struct FileLocks g_locks = { static struct FileLocks g_locks;
.mu = PTHREAD_MUTEX_INITIALIZER,
};
static textwindows struct FileLock *NewFileLock(void) { static textwindows struct FileLock *NewFileLock(void) {
struct FileLock *fl; struct FileLock *fl;
@ -113,7 +110,7 @@ static textwindows bool EqualsFileLock(struct FileLock *fl, int64_t off,
textwindows void sys_fcntl_nt_lock_cleanup(int fd) { textwindows void sys_fcntl_nt_lock_cleanup(int fd) {
struct FileLock *fl, *ft, **flp; struct FileLock *fl, *ft, **flp;
_pthread_mutex_lock(&g_locks.mu); pthread_mutex_lock(&g_locks.mu);
for (flp = &g_locks.list, fl = *flp; fl;) { for (flp = &g_locks.list, fl = *flp; fl;) {
if (fl->fd == fd) { if (fl->fd == fd) {
*flp = fl->next; *flp = fl->next;
@ -125,7 +122,7 @@ textwindows void sys_fcntl_nt_lock_cleanup(int fd) {
fl = *flp; fl = *flp;
} }
} }
_pthread_mutex_unlock(&g_locks.mu); pthread_mutex_unlock(&g_locks.mu);
} }
static textwindows int64_t GetfileSize(int64_t handle) { static textwindows int64_t GetfileSize(int64_t handle) {
@ -356,9 +353,9 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
} else if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) { } else if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) {
struct Fd *f = g_fds.p + fd; struct Fd *f = g_fds.p + fd;
if (f->cursor) { if (f->cursor) {
_pthread_mutex_lock(&g_locks.mu); pthread_mutex_lock(&g_locks.mu);
rc = sys_fcntl_nt_lock(f, fd, cmd, arg); rc = sys_fcntl_nt_lock(f, fd, cmd, arg);
_pthread_mutex_unlock(&g_locks.mu); pthread_mutex_unlock(&g_locks.mu);
} else { } else {
rc = ebadf(); rc = ebadf();
} }

View file

@ -26,9 +26,7 @@
// @see setcontext() // @see setcontext()
.ftrace1 .ftrace1
getcontext: getcontext:
beg
.ftrace2 .ftrace2
#include "libc/intrin/getcontext.inc" #include "libc/calls/getcontext.inc"
jmp __getcontextsig jmp __getcontextsig
end
.endfn getcontext,globl .endfn getcontext,globl

View file

@ -34,7 +34,6 @@
mov %rbp,120(%rdi) mov %rbp,120(%rdi)
mov %rbx,128(%rdi) mov %rbx,128(%rdi)
mov %rdx,136(%rdi) mov %rdx,136(%rdi)
mov %rax,144(%rdi)
mov %rcx,152(%rdi) mov %rcx,152(%rdi)
lea 8(%rsp),%rax lea 8(%rsp),%rax
mov %rax,160(%rdi) // rsp = caller's rsp mov %rax,160(%rdi) // rsp = caller's rsp

View file

@ -21,23 +21,24 @@
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/fmt/conv.h" #include "libc/fmt/conv.h"
#include "libc/intrin/cxaatexit.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/nt/accounting.h" #include "libc/nt/accounting.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#define CTOR __attribute__((__constructor__(99)))
#define FT(x) (x.dwLowDateTime | (uint64_t)x.dwHighDateTime << 32) #define FT(x) (x.dwLowDateTime | (uint64_t)x.dwHighDateTime << 32)
static int cpus; static int cpus;
static double load; static double load;
static pthread_spinlock_t lock;
static struct NtFileTime idle1, kern1, user1; static struct NtFileTime idle1, kern1, user1;
textwindows int sys_getloadavg_nt(double *a, int n) { textwindows int sys_getloadavg_nt(double *a, int n) {
int i, rc; int i, rc;
uint64_t elapsed, used; uint64_t elapsed, used;
struct NtFileTime idle, kern, user; struct NtFileTime idle, kern, user;
__cxa_lock(); BLOCK_SIGNALS;
pthread_spin_lock(&lock);
if (GetSystemTimes(&idle, &kern, &user)) { if (GetSystemTimes(&idle, &kern, &user)) {
elapsed = (FT(kern) - FT(kern1)) + (FT(user) - FT(user1)); elapsed = (FT(kern) - FT(kern1)) + (FT(user) - FT(user1));
if (elapsed) { if (elapsed) {
@ -53,11 +54,12 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
} else { } else {
rc = __winerr(); rc = __winerr();
} }
__cxa_unlock(); pthread_spin_unlock(&lock);
ALLOW_SIGNALS;
return rc; return rc;
} }
CTOR static textstartup void sys_getloadavg_nt_init(void) { __attribute__((__constructor__(40))) static textstartup void ntinitload(void) {
if (IsWindows()) { if (IsWindows()) {
load = 1; load = 1;
cpus = __get_cpu_count() / 2; cpus = __get_cpu_count() / 2;

View file

@ -28,8 +28,8 @@
// @return rdi is rdi+edx // @return rdi is rdi+edx
.text.startup .text.startup
__getntsyspath: __getntsyspath:
beg push %rbp
pro mov %rsp,%rbp
push %rdx push %rdx
movpp %rdi,%rcx # call f=%rax(p1=%rcx,p2=%rdx) movpp %rdi,%rcx # call f=%rax(p1=%rcx,p2=%rdx)
sub $40,%rsp sub $40,%rsp
@ -55,7 +55,6 @@ __getntsyspath:
jne 2f jne 2f
movb $'/',-1(%rdi) movb $'/',-1(%rdi)
2: .loop 1b 2: .loop 1b
epi leave
ret ret
end
.endfn __getntsyspath,globl,hidden .endfn __getntsyspath,globl,hidden

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ /*-*- 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 vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the
@ -16,14 +16,22 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/nt/memory.h" #include "libc/calls/syscall-nt.internal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/nt/process.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/nt/struct/processbasicinformation.h"
/** textwindows int sys_getppid_nt(void) {
* Allocates memory on The New Technology. struct NtProcessBasicInformation ProcessInformation;
*/ uint32_t gotsize = 0;
textwindows void *VirtualAlloc(void *lpAddress, uint64_t dwSize, if (!NtError(
uint32_t flAllocationType, uint32_t flProtect) { NtQueryInformationProcess(GetCurrentProcess(), 0, &ProcessInformation,
return VirtualAllocEx(GetCurrentProcess(), lpAddress, dwSize, sizeof(ProcessInformation), &gotsize)) &&
flAllocationType, flProtect); gotsize >= sizeof(ProcessInformation) &&
ProcessInformation.InheritedFromUniqueProcessId) {
return ProcessInformation.InheritedFromUniqueProcessId;
}
return GetCurrentProcessId();
} }

View file

@ -96,8 +96,9 @@ static int OldApeLoader(char *s) {
static int CopyWithCwd(const char *q, char *p, char *e) { static int CopyWithCwd(const char *q, char *p, char *e) {
char c; char c;
if (*q != '/') { if (*q != '/') {
if (q[0] == '.' && q[1] == '/') if (q[0] == '.' && q[1] == '/') {
q += 2; q += 2;
}
int got = __getcwd(p, e - p - 1 /* '/' */); int got = __getcwd(p, e - p - 1 /* '/' */);
if (got != -1) { if (got != -1) {
p += got - 1; p += got - 1;
@ -117,10 +118,9 @@ static int CopyWithCwd(const char *q, char *p, char *e) {
// if q exists then turn it into an absolute path. // if q exists then turn it into an absolute path.
static int TryPath(const char *q) { static int TryPath(const char *q) {
if (!q) if (!CopyWithCwd(q, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf))) {
return 0;
if (!CopyWithCwd(q, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf)))
return 0; return 0;
}
return !sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0); return !sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0);
} }
@ -129,8 +129,9 @@ static int TryPath(const char *q) {
void __init_program_executable_name(void) { void __init_program_executable_name(void) {
if (__program_executable_name && *__program_executable_name != '/' && if (__program_executable_name && *__program_executable_name != '/' &&
CopyWithCwd(__program_executable_name, g_prog.u.buf, CopyWithCwd(__program_executable_name, g_prog.u.buf,
g_prog.u.buf + sizeof(g_prog.u.buf))) g_prog.u.buf + sizeof(g_prog.u.buf))) {
__program_executable_name = g_prog.u.buf; __program_executable_name = g_prog.u.buf;
}
} }
static inline void InitProgramExecutableNameImpl(void) { static inline void InitProgramExecutableNameImpl(void) {
@ -211,12 +212,14 @@ static inline void InitProgramExecutableNameImpl(void) {
} }
// don't trust argv or envp if set-id. // don't trust argv or envp if set-id.
if (issetugid()) if (issetugid()) {
goto UseEmpty; goto UseEmpty;
}
// try argv[0], then then $_. // try argv[0], then then $_.
if (TryPath(__argv[0]) || TryPath(__getenv(__envp, "_").s)) if (TryPath(__argv[0]) || TryPath(__getenv(__envp, "_").s)) {
goto UseBuf; goto UseBuf;
}
// give up and just copy argv[0] into it // give up and just copy argv[0] into it
if ((q = __argv[0])) { if ((q = __argv[0])) {

View file

@ -114,8 +114,7 @@ static ssize_t GetDevUrandom(char *p, size_t n, unsigned f) {
ssize_t __getrandom(void *p, size_t n, unsigned f) { ssize_t __getrandom(void *p, size_t n, unsigned f) {
ssize_t rc; ssize_t rc;
if (IsWindows()) { if (IsWindows()) {
ProcessPrng(p, n); // never fails rc = ProcessPrng(p, n) ? n : __winerr();
rc = n;
} else if (have_getrandom) { } else if (have_getrandom) {
if (IsXnu() || IsOpenbsd()) { if (IsXnu() || IsOpenbsd()) {
rc = GetRandomBsd(p, n, GetRandomEntropy); rc = GetRandomBsd(p, n, GetRandomEntropy);
@ -185,7 +184,9 @@ ssize_t __getrandom(void *p, size_t n, unsigned f) {
* @raise EFAULT if the `n` bytes at `p` aren't valid memory * @raise EFAULT if the `n` bytes at `p` aren't valid memory
* @raise EINTR if we needed to block and a signal was delivered instead * @raise EINTR if we needed to block and a signal was delivered instead
* @cancelationpoint * @cancelationpoint
* @asyncsignalsafe
* @restartable * @restartable
* @vforksafe
*/ */
ssize_t getrandom(void *p, size_t n, unsigned f) { ssize_t getrandom(void *p, size_t n, unsigned f) {
ssize_t rc; ssize_t rc;

View file

@ -21,7 +21,6 @@
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/describeflags.h" #include "libc/intrin/describeflags.h"
#include "libc/intrin/rlimit.h"
#include "libc/intrin/strace.h" #include "libc/intrin/strace.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h" #include "libc/runtime/stack.h"
@ -48,7 +47,8 @@ int getrlimit(int resource, struct rlimit *rlim) {
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_getrlimit(resource, rlim); rc = sys_getrlimit(resource, rlim);
} else if (resource == RLIMIT_STACK) { } else if (resource == RLIMIT_STACK) {
*rlim = __rlimit_stack_get(); rlim->rlim_cur = GetStaticStackSize();
rlim->rlim_max = GetStaticStackSize();
rc = 0; rc = 0;
} else if (resource == RLIMIT_AS) { } else if (resource == RLIMIT_AS) {
rlim->rlim_cur = __virtualmax; rlim->rlim_cur = __virtualmax;

View file

@ -3,7 +3,6 @@
#include "libc/atomic.h" #include "libc/atomic.h"
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigval.h" #include "libc/calls/struct/sigval.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/fds.h" #include "libc/intrin/fds.h"
#include "libc/macros.h" #include "libc/macros.h"
@ -31,10 +30,8 @@ int CountConsoleInputBytes(void);
int FlushConsoleInputBytes(void); int FlushConsoleInputBytes(void);
int64_t GetConsoleInputHandle(void); int64_t GetConsoleInputHandle(void);
int64_t GetConsoleOutputHandle(void); int64_t GetConsoleOutputHandle(void);
void EchoConsoleNt(const char *, size_t, bool);
int IsWindowsExecutable(int64_t, const char16_t *); int IsWindowsExecutable(int64_t, const char16_t *);
void InterceptTerminalCommands(const char *, size_t); void InterceptTerminalCommands(const char *, size_t);
void sys_read_nt_wipe_keystrokes(void);
forceinline bool __isfdopen(int fd) { forceinline bool __isfdopen(int fd) {
return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty; return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty;
@ -48,8 +45,8 @@ int _check_signal(bool);
int _check_cancel(void); int _check_cancel(void);
bool _is_canceled(void); bool _is_canceled(void);
int sys_close_nt(int, int); int sys_close_nt(int, int);
int _park_norestart(struct timespec, uint64_t); int _park_norestart(uint32_t, uint64_t);
int _park_restartable(struct timespec, uint64_t); int _park_restartable(uint32_t, uint64_t);
int sys_openat_metal(int, const char *, int, unsigned); int sys_openat_metal(int, const char *, int, unsigned);
#ifdef __x86_64__ #ifdef __x86_64__

View file

@ -67,9 +67,10 @@ textstartup void InitializeMetalFile(void) {
size_t size = ROUNDUP(_ezip - __executable_start, 4096); size_t size = ROUNDUP(_ezip - __executable_start, 4096);
// TODO(jart): Restore support for ZIPOS on metal. // TODO(jart): Restore support for ZIPOS on metal.
void *copied_base; void *copied_base;
void *addr = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE, struct DirectMap dm;
dm = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED_linux | MAP_ANONYMOUS_linux, -1, 0); MAP_SHARED_linux | MAP_ANONYMOUS_linux, -1, 0);
copied_base = addr; copied_base = dm.addr;
npassert(copied_base != (void *)-1); npassert(copied_base != (void *)-1);
memcpy(copied_base, (void *)(BANE + IMAGE_BASE_PHYSICAL), size); memcpy(copied_base, (void *)(BANE + IMAGE_BASE_PHYSICAL), size);
__ape_com_base = copied_base; __ape_com_base = copied_base;

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/proc/ntspawn.h" #include "libc/proc/ntspawn.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/strace.h" #include "libc/intrin/strace.h"
@ -39,15 +38,12 @@
#include "libc/nt/struct/procthreadattributelist.h" #include "libc/nt/struct/procthreadattributelist.h"
#include "libc/nt/struct/startupinfo.h" #include "libc/nt/struct/startupinfo.h"
#include "libc/nt/struct/startupinfoex.h" #include "libc/nt/struct/startupinfoex.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/proc/ntspawn.h" #include "libc/proc/ntspawn.h"
#include "libc/stdalign.h" #include "libc/stdalign.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#ifdef __x86_64__ #ifdef __x86_64__
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
struct SpawnBlock { struct SpawnBlock {
char16_t path[PATH_MAX]; char16_t path[PATH_MAX];
char16_t cmdline[32767]; char16_t cmdline[32767];
@ -67,12 +63,10 @@ static textwindows ssize_t ntspawn_read(intptr_t fh, char *buf, size_t len) {
bool ok; bool ok;
uint32_t got; uint32_t got;
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0)}; struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0)};
ok = overlap.hEvent && ok = (ReadFile(fh, buf, len, 0, &overlap) ||
(ReadFile(fh, buf, len, 0, &overlap) ||
GetLastError() == kNtErrorIoPending) && GetLastError() == kNtErrorIoPending) &&
GetOverlappedResult(fh, &overlap, &got, true); GetOverlappedResult(fh, &overlap, &got, true);
if (overlap.hEvent) CloseHandle(overlap.hEvent);
__imp_CloseHandle(overlap.hEvent);
return ok ? got : -1; return ok ? got : -1;
} }
@ -92,7 +86,7 @@ static textwindows int ntspawn2(struct NtSpawnArgs *a, struct SpawnBlock *sb) {
if (fh == -1) if (fh == -1)
return -1; return -1;
ssize_t got = ntspawn_read(fh, p, pe - p); ssize_t got = ntspawn_read(fh, p, pe - p);
__imp_CloseHandle(fh); CloseHandle(fh);
if (got < 3) if (got < 3)
return enoexec(); return enoexec();
pe = p + got; pe = p + got;

View file

@ -49,9 +49,11 @@ int sys_openat_metal(int dirfd, const char *file, int flags, unsigned mode) {
if ((fd = __reservefd(-1)) == -1) if ((fd = __reservefd(-1)) == -1)
return -1; return -1;
if (!_weaken(calloc) || !_weaken(free)) { if (!_weaken(calloc) || !_weaken(free)) {
state = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096), struct DirectMap dm;
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, dm = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096),
-1, 0); PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1,
0);
state = dm.addr;
if (state == (void *)-1) if (state == (void *)-1)
return -1; return -1;
} else { } else {

View file

@ -19,78 +19,47 @@
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h" #include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/wintime.internal.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/events.h" #include "libc/nt/events.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h" #include "libc/nt/synchronization.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sicode.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h" #include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__ #ifdef __x86_64__
// returns 0 if deadline is reached // returns 0 on timeout or spurious wakeup
// raises EINTR if a signal delivery interrupted wait operation // raises EINTR if a signal delivery interrupted wait operation
// raises ECANCELED if this POSIX thread was canceled in masked mode // raises ECANCELED if this POSIX thread was canceled in masked mode
textwindows static int _park_thread(struct timespec deadline, sigset_t waitmask, textwindows static int _park_thread(uint32_t msdelay, sigset_t waitmask,
bool restartable) { bool restartable) {
for (;;) { struct PosixThread *pt = _pthread_self();
uint32_t handl = 0;
intptr_t hands[2];
// create event object // perform the wait operation
intptr_t sigev; intptr_t sigev;
if (!(sigev = CreateEvent(0, 0, 0, 0))) if (!(sigev = CreateEvent(0, 0, 0, 0)))
return __winerr(); return __winerr();
hands[handl++] = sigev;
// create high precision timer if needed
if (memcmp(&deadline, &timespec_max, sizeof(struct timespec))) {
intptr_t hTimer;
if ((hTimer = CreateWaitableTimer(NULL, true, NULL))) {
int64_t due = TimeSpecToWindowsTime(deadline);
if (SetWaitableTimer(hTimer, &due, 0, NULL, NULL, false)) {
hands[handl++] = hTimer;
} else {
CloseHandle(hTimer);
}
}
}
// perform wait operation
struct PosixThread *pt = _pthread_self();
pt->pt_event = sigev; pt->pt_event = sigev;
pt->pt_blkmask = waitmask; pt->pt_blkmask = waitmask;
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT, atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT,
memory_order_release); memory_order_release);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!// //!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
int sig = 0; int sig = 0;
uint32_t wi = 0; uint32_t ws = 0;
if (!_is_canceled() && if (!_is_canceled() &&
!(_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))) !(_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))))
wi = WaitForMultipleObjects(handl, hands, false, -1u); ws = WaitForSingleObject(sigev, msdelay);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!// //!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release); atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
for (int i = 0; i < handl; ++i) CloseHandle(sigev);
CloseHandle(hands[i]);
// recursion is now safe // recursion is now safe
if (wi == 1) if (ws == -1u)
return 0;
if (wi == -1u)
return __winerr(); return __winerr();
int handler_was_called = 0; int handler_was_called = 0;
if (!sig) {
if (_check_cancel())
return -1;
if (_weaken(__sig_get))
sig = _weaken(__sig_get)(waitmask);
}
if (sig) if (sig)
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask); handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel()) if (_check_cancel())
@ -100,15 +69,15 @@ textwindows static int _park_thread(struct timespec deadline, sigset_t waitmask,
if (handler_was_called & SIG_HANDLED_SA_RESTART) if (handler_was_called & SIG_HANDLED_SA_RESTART)
if (!restartable) if (!restartable)
return eintr(); return eintr();
} return 0;
} }
textwindows int _park_norestart(struct timespec deadline, sigset_t waitmask) { textwindows int _park_norestart(uint32_t msdelay, sigset_t waitmask) {
return _park_thread(deadline, waitmask, false); return _park_thread(msdelay, waitmask, false);
} }
textwindows int _park_restartable(struct timespec deadline, sigset_t waitmask) { textwindows int _park_restartable(uint32_t msdelay, sigset_t waitmask) {
return _park_thread(deadline, waitmask, true); return _park_thread(msdelay, waitmask, true);
} }
#endif /* __x86_64__ */ #endif /* __x86_64__ */

View file

@ -18,20 +18,21 @@
*/ */
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#ifdef __x86_64__ #ifdef __x86_64__
textwindows int sys_pause_nt(void) { textwindows int sys_pause_nt(void) {
int rc;
// we don't strictly need to block signals, but it reduces signal // we don't strictly need to block signals, but it reduces signal
// delivery latency, by preventing other threads from delivering a // delivery latency, by preventing other threads from delivering a
// signal asynchronously. it takes about ~5us to deliver a signal // signal asynchronously. it takes about ~5us to deliver a signal
// using SetEvent() whereas it takes ~30us to use SuspendThread(), // using SetEvent() whereas it takes ~30us to use SuspendThread(),
// GetThreadContext(), SetThreadContext(), and ResumeThread(). // GetThreadContext(), SetThreadContext(), and ResumeThread().
BLOCK_SIGNALS; BLOCK_SIGNALS;
_park_norestart(timespec_max, 0); while (!(rc = _park_norestart(-1u, 0)))
donothing;
ALLOW_SIGNALS; ALLOW_SIGNALS;
return -1; return rc;
} }
#endif /* __x86_64__ */ #endif /* __x86_64__ */

View file

@ -694,7 +694,6 @@ static const uint16_t kPledgeStdio[] = {
__NR_linux_sched_getaffinity, // __NR_linux_sched_getaffinity, //
__NR_linux_sched_setaffinity, // __NR_linux_sched_setaffinity, //
__NR_linux_sigtimedwait, // __NR_linux_sigtimedwait, //
__NR_linux_getcpu, //
}; };
static const uint16_t kPledgeFlock[] = { static const uint16_t kPledgeFlock[] = {

View file

@ -318,8 +318,8 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds, textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
struct timespec deadline, struct timespec deadline,
const sigset_t waitmask) { const sigset_t waitmask) {
uint32_t waitms;
int i, n, rc, got = 0; int i, n, rc, got = 0;
struct timespec now, next, target;
// we normally don't check for signals until we decide to wait, since // we normally don't check for signals until we decide to wait, since
// it's nice to have functions like write() be unlikely to EINTR, but // it's nice to have functions like write() be unlikely to EINTR, but
@ -344,16 +344,9 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
} }
if (got) if (got)
return got; return got;
now = sys_clock_gettime_monotonic_nt(); if (!(waitms = sys_poll_nt_waitms(deadline)))
if (timespec_cmp(now, deadline) >= 0)
return 0; return 0;
next = timespec_add(now, timespec_frommillis(POLL_INTERVAL_MS)); if (_park_norestart(waitms, waitmask) == -1)
if (timespec_cmp(next, deadline) >= 0) {
target = deadline;
} else {
target = next;
}
if (_park_norestart(target, waitmask) == -1)
return -1; return -1;
} }
} }

View file

@ -48,7 +48,6 @@
#include "libc/nt/enum/wait.h" #include "libc/nt/enum/wait.h"
#include "libc/nt/errors.h" #include "libc/nt/errors.h"
#include "libc/nt/events.h" #include "libc/nt/events.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/nt/struct/inputrecord.h" #include "libc/nt/struct/inputrecord.h"
#include "libc/nt/synchronization.h" #include "libc/nt/synchronization.h"
@ -128,46 +127,33 @@ struct Keystrokes {
bool ohno_decckm; bool ohno_decckm;
bool bypass_mode; bool bypass_mode;
uint16_t utf16hs; uint16_t utf16hs;
size_t free_keys; int16_t freekeys;
int64_t cin, cot; int64_t cin, cot;
struct Dll *list; struct Dll *list;
struct Dll *line; struct Dll *line;
struct Dll *free; struct Dll *free;
pthread_mutex_t lock;
struct Keystroke pool[512];
}; };
static struct Keystrokes __keystroke; static struct Keystrokes __keystroke;
static pthread_mutex_t __keystroke_lock = PTHREAD_MUTEX_INITIALIZER;
textwindows void sys_read_nt_wipe_keystrokes(void) { textwindows void WipeKeystrokes(void) {
bzero(&__keystroke, sizeof(__keystroke)); bzero(&__keystroke, sizeof(__keystroke));
_pthread_mutex_wipe_np(&__keystroke_lock);
} }
textwindows static void FreeKeystrokeImpl(struct Dll *key) { textwindows static void FreeKeystrokeImpl(struct Dll *key) {
dll_make_first(&__keystroke.free, key); dll_make_first(&__keystroke.free, key);
++__keystroke.free_keys; ++__keystroke.freekeys;
}
textwindows static struct Keystroke *AllocKeystroke(void) {
struct Keystroke *k;
if (!(k = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Keystroke))))
return 0;
dll_init(&k->elem);
return k;
} }
textwindows static struct Keystroke *NewKeystroke(void) { textwindows static struct Keystroke *NewKeystroke(void) {
struct Dll *e; struct Dll *e = dll_first(__keystroke.free);
struct Keystroke *k; if (!e) // See MIN(freekeys) before ReadConsoleInput()
if ((e = dll_first(__keystroke.free))) { __builtin_trap();
dll_remove(&__keystroke.free, e); struct Keystroke *k = KEYSTROKE_CONTAINER(e);
k = KEYSTROKE_CONTAINER(e); dll_remove(&__keystroke.free, &k->elem);
--__keystroke.free_keys; --__keystroke.freekeys;
} else {
// PopulateKeystrokes() should make this branch impossible
if (!(k = AllocKeystroke()))
return 0;
}
k->buflen = 0; k->buflen = 0;
return k; return k;
} }
@ -183,22 +169,15 @@ textwindows static void FreeKeystrokes(struct Dll **list) {
FreeKeystroke(list, key); FreeKeystroke(list, key);
} }
textwindows static void PopulateKeystrokes(size_t want) {
struct Keystroke *k;
while (__keystroke.free_keys < want) {
if ((k = AllocKeystroke())) {
FreeKeystrokeImpl(&k->elem);
} else {
break;
}
}
}
textwindows static void OpenConsole(void) { textwindows static void OpenConsole(void) {
__keystroke.cin = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite, __keystroke.cin = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
kNtFileShareRead, 0, kNtOpenExisting, 0, 0); kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
__keystroke.cot = CreateFile(u"CONOUT$", kNtGenericRead | kNtGenericWrite, __keystroke.cot = CreateFile(u"CONOUT$", kNtGenericRead | kNtGenericWrite,
kNtFileShareWrite, 0, kNtOpenExisting, 0, 0); kNtFileShareWrite, 0, kNtOpenExisting, 0, 0);
for (int i = 0; i < ARRAYLEN(__keystroke.pool); ++i) {
dll_init(&__keystroke.pool[i].elem);
FreeKeystrokeImpl(&__keystroke.pool[i].elem);
}
} }
textwindows static int AddSignal(int sig) { textwindows static int AddSignal(int sig) {
@ -212,11 +191,11 @@ textwindows static void InitConsole(void) {
} }
textwindows static void LockKeystrokes(void) { textwindows static void LockKeystrokes(void) {
_pthread_mutex_lock(&__keystroke_lock); pthread_mutex_lock(&__keystroke.lock);
} }
textwindows static void UnlockKeystrokes(void) { textwindows static void UnlockKeystrokes(void) {
_pthread_mutex_unlock(&__keystroke_lock); pthread_mutex_unlock(&__keystroke.lock);
} }
textwindows int64_t GetConsoleInputHandle(void) { textwindows int64_t GetConsoleInputHandle(void) {
@ -341,12 +320,9 @@ textwindows static int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
// note we define _POSIX_VDISABLE as zero // note we define _POSIX_VDISABLE as zero
// tcsetattr() lets anyone reconfigure these keybindings // tcsetattr() lets anyone reconfigure these keybindings
if (c && !(__ttyconf.magic & kTtyNoIsigs) && !__keystroke.bypass_mode) { if (c && !(__ttyconf.magic & kTtyNoIsigs) && !__keystroke.bypass_mode) {
char b[] = {c};
if (c == __ttyconf.vintr) { if (c == __ttyconf.vintr) {
EchoConsoleNt(b, 1, false);
return AddSignal(SIGINT); return AddSignal(SIGINT);
} else if (c == __ttyconf.vquit) { } else if (c == __ttyconf.vquit) {
EchoConsoleNt(b, 1, false);
return AddSignal(SIGQUIT); return AddSignal(SIGQUIT);
} }
} }
@ -481,8 +457,7 @@ textwindows static void WriteCtl(const char *p, size_t n, bool escape_harder) {
} }
} }
textwindows void EchoConsoleNt(const char *p, size_t n, bool escape_harder) { textwindows static void EchoTty(const char *p, size_t n, bool escape_harder) {
InitConsole();
if (!(__ttyconf.magic & kTtySilence)) { if (!(__ttyconf.magic & kTtySilence)) {
if (__ttyconf.magic & kTtyEchoRaw) { if (__ttyconf.magic & kTtyEchoRaw) {
WriteTty(p, n); WriteTty(p, n);
@ -539,12 +514,14 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
!(__ttyconf.magic & kTtyNoIexten)) { // IEXTEN !(__ttyconf.magic & kTtyNoIexten)) { // IEXTEN
if (__keystroke.bypass_mode) { if (__keystroke.bypass_mode) {
struct Keystroke *k = NewKeystroke(); struct Keystroke *k = NewKeystroke();
if (!k)
return;
memcpy(k->buf, buf, sizeof(k->buf)); memcpy(k->buf, buf, sizeof(k->buf));
k->buflen = len; k->buflen = len;
dll_make_last(&__keystroke.line, &k->elem); dll_make_last(&__keystroke.line, &k->elem);
EchoConsoleNt(buf, len, true); EchoTty(buf, len, true);
if (!__keystroke.freekeys) {
dll_make_last(&__keystroke.list, __keystroke.line);
__keystroke.line = 0;
}
__keystroke.bypass_mode = false; __keystroke.bypass_mode = false;
return; return;
} else if (len == 1 && buf[0] && // } else if (len == 1 && buf[0] && //
@ -634,14 +611,12 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
// allocate object to hold keystroke // allocate object to hold keystroke
struct Keystroke *k = NewKeystroke(); struct Keystroke *k = NewKeystroke();
if (!k)
return;
memcpy(k->buf, buf, sizeof(k->buf)); memcpy(k->buf, buf, sizeof(k->buf));
k->buflen = len; k->buflen = len;
// echo input if it was successfully recorded // echo input if it was successfully recorded
// assuming the win32 console isn't doing it already // assuming the win32 console isn't doing it already
EchoConsoleNt(buf, len, false); EchoTty(buf, len, false);
// save keystroke to appropriate list // save keystroke to appropriate list
if (__ttyconf.magic & kTtyUncanon) { if (__ttyconf.magic & kTtyUncanon) {
@ -649,12 +624,12 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
} else { } else {
dll_make_last(&__keystroke.line, &k->elem); dll_make_last(&__keystroke.line, &k->elem);
// flush canonical mode line on enter // flush canonical mode line if oom or enter
if (len == 1 && buf[0] && if (!__keystroke.freekeys || (len == 1 && buf[0] &&
((buf[0] & 255) == '\n' || // ((buf[0] & 255) == '\n' || //
(buf[0] & 255) == __ttyconf.veol || // (buf[0] & 255) == __ttyconf.veol || //
((buf[0] & 255) == __ttyconf.veol2 && ((buf[0] & 255) == __ttyconf.veol2 &&
!(__ttyconf.magic & kTtyNoIexten)))) { !(__ttyconf.magic & kTtyNoIexten))))) {
dll_make_last(&__keystroke.list, __keystroke.line); dll_make_last(&__keystroke.list, __keystroke.line);
__keystroke.line = 0; __keystroke.line = 0;
} }
@ -665,17 +640,15 @@ textwindows static void IngestConsoleInput(void) {
uint32_t i, n; uint32_t i, n;
struct NtInputRecord records[16]; struct NtInputRecord records[16];
for (;;) { for (;;) {
if (!__keystroke.freekeys)
return;
if (__keystroke.end_of_file) if (__keystroke.end_of_file)
return; return;
if (!GetNumberOfConsoleInputEvents(__keystroke.cin, &n)) if (!GetNumberOfConsoleInputEvents(__keystroke.cin, &n))
goto UnexpectedEof; goto UnexpectedEof;
if (n > ARRAYLEN(records)) if (!n || !__keystroke.freekeys)
n = ARRAYLEN(records);
PopulateKeystrokes(n + 1);
if (n > __keystroke.free_keys)
n = __keystroke.free_keys;
if (!n)
return; return;
n = MIN(__keystroke.freekeys, MIN(ARRAYLEN(records), n));
if (!ReadConsoleInput(__keystroke.cin, records, n, &n)) if (!ReadConsoleInput(__keystroke.cin, records, n, &n))
goto UnexpectedEof; goto UnexpectedEof;
for (i = 0; i < n && !__keystroke.end_of_file; ++i) for (i = 0; i < n && !__keystroke.end_of_file; ++i)
@ -997,10 +970,8 @@ textwindows ssize_t ReadBuffer(int fd, void *data, size_t size, int64_t offset,
if (f->kind == kFdDevNull) if (f->kind == kFdDevNull)
return 0; return 0;
if (f->kind == kFdDevRandom) { if (f->kind == kFdDevRandom)
ProcessPrng(data, size); return ProcessPrng(data, size) ? size : __winerr();
return size;
}
if (f->kind == kFdConsole) if (f->kind == kFdConsole)
return ReadFromConsole(f, data, size, waitmask); return ReadFromConsole(f, data, size, waitmask);

View file

@ -23,7 +23,6 @@
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/describeflags.h" #include "libc/intrin/describeflags.h"
#include "libc/intrin/rlimit.h"
#include "libc/intrin/strace.h" #include "libc/intrin/strace.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
@ -89,12 +88,10 @@ int setrlimit(int resource, const struct rlimit *rlim) {
} else if (!IsWindows() && !(IsNetbsd() && resource == RLIMIT_AS)) { } else if (!IsWindows() && !(IsNetbsd() && resource == RLIMIT_AS)) {
rc = sys_setrlimit(resource, rlim); rc = sys_setrlimit(resource, rlim);
} else if (resource == RLIMIT_STACK) { } else if (resource == RLIMIT_STACK) {
rc = 0; rc = enotsup();
} else { } else {
rc = einval(); rc = einval();
} }
if (!rc && resource == RLIMIT_STACK)
__rlimit_stack_set(*rlim); // so __rlimit_stack_get() works on all OSes
if (resource == RLIMIT_AS) { if (resource == RLIMIT_AS) {
__virtualmax = rlim->rlim_cur; __virtualmax = rlim->rlim_cur;
errno = olde; errno = olde;

View file

@ -35,8 +35,9 @@ void shm_path_np(const char *name, char buf[hasatleast 78]) {
const char *a; const char *a;
uint8_t digest[BLAKE2B256_DIGEST_LENGTH]; uint8_t digest[BLAKE2B256_DIGEST_LENGTH];
a = "/tmp/", n = 5; a = "/tmp/", n = 5;
if (IsLinux() && isdirectory("/dev/shm")) if (IsLinux() && isdirectory("/dev/shm")) {
a = "/dev/shm/", n = 9; a = "/dev/shm/", n = 9;
}
BLAKE2B256(name, strlen(name), digest); BLAKE2B256(name, strlen(name), digest);
p = mempcpy(buf, a, n); p = mempcpy(buf, a, n);
p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH); p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH);

717
libc/calls/sig.c Normal file
View file

@ -0,0 +1,717 @@
/*-*- 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 2022 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 "libc/sysv/consts/sig.h"
#include "ape/sections.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bsf.h"
#include "libc/intrin/describebacktrace.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/maps.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/context.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/events.h"
#include "libc/nt/runtime.h"
#include "libc/nt/signals.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/ss.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
/**
* @fileoverview Cosmopolitan Signals for Windows.
*/
#define STKSZ 65536
struct SignalFrame {
unsigned rva;
unsigned flags;
siginfo_t si;
ucontext_t ctx;
};
static textwindows bool __sig_ignored_by_default(int sig) {
return sig == SIGURG || //
sig == SIGCONT || //
sig == SIGCHLD || //
sig == SIGWINCH;
}
textwindows bool __sig_ignored(int sig) {
return __sighandrvas[sig] == (intptr_t)SIG_IGN ||
(__sighandrvas[sig] == (intptr_t)SIG_DFL &&
__sig_ignored_by_default(sig));
}
textwindows void __sig_delete(int sig) {
struct Dll *e;
atomic_fetch_and_explicit(__sig.process, ~(1ull << (sig - 1)),
memory_order_relaxed);
_pthread_lock();
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e))
atomic_fetch_and_explicit(&POSIXTHREAD_CONTAINER(e)->tib->tib_sigpending,
~(1ull << (sig - 1)), memory_order_relaxed);
_pthread_unlock();
}
static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
int sig;
sigset_t bit, pending, deliverable;
for (;;) {
pending = atomic_load_explicit(sigs, memory_order_acquire);
if ((deliverable = pending & ~masked)) {
sig = bsfl(deliverable) + 1;
bit = 1ull << (sig - 1);
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit)
return sig;
} else {
return 0;
}
}
}
textwindows int __sig_get(sigset_t masked) {
int sig;
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked)))
sig = __sig_getter(__sig.process, masked);
return sig;
}
static textwindows bool __sig_should_use_altstack(unsigned flags,
struct CosmoTib *tib) {
if (!(flags & SA_ONSTACK))
return false; // signal handler didn't enable it
if (!tib->tib_sigstack_size)
return false; // sigaltstack() wasn't installed on this thread
if (tib->tib_sigstack_flags & SS_DISABLE)
return false; // sigaltstack() on this thread was disabled by user
char *bp = __builtin_frame_address(0);
if (tib->tib_sigstack_addr <= bp &&
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size)
return false; // we're already on the alternate stack
return true;
}
static textwindows wontreturn void __sig_terminate(int sig) {
TerminateThisProcess(sig);
}
textwindows static bool __sig_wake(struct PosixThread *pt, int sig) {
atomic_int *blocker;
blocker = atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
if (!blocker)
return false;
// threads can create semaphores on an as-needed basis
if (blocker == PT_BLOCKER_EVENT) {
STRACE("%G set %d's event object", sig, _pthread_tid(pt));
SetEvent(pt->pt_event);
return !!atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
}
// all other blocking ops that aren't overlap should use futexes
// we force restartable futexes to churn by waking w/o releasing
STRACE("%G waking %d's futex", sig, _pthread_tid(pt));
WakeByAddressSingle(blocker);
return !!atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
}
textwindows static bool __sig_start(struct PosixThread *pt, int sig,
unsigned *rva, unsigned *flags) {
*rva = __sighandrvas[sig];
*flags = __sighandflags[sig];
if (*rva == (intptr_t)SIG_IGN ||
(*rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
STRACE("ignoring %G", sig);
return false;
}
if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1))) {
STRACE("enqueing %G on %d", sig, _pthread_tid(pt));
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_relaxed);
__sig_wake(pt, sig);
return false;
}
if (*rva == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
return true;
}
textwindows static sigaction_f __sig_handler(unsigned rva) {
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
return (sigaction_f)(__executable_start + rva);
}
textwindows int __sig_raise(volatile int sig, int sic) {
// bitset of kinds of handlers called
volatile int handler_was_called = 0;
// loop over pending signals
ucontext_t ctx;
getcontext(&ctx);
if (!sig) {
if ((sig = __sig_get(ctx.uc_sigmask))) {
sic = SI_KERNEL;
} else {
return handler_was_called;
}
}
// process signal(s)
unsigned rva, flags;
struct PosixThread *pt = _pthread_self();
if (__sig_start(pt, sig, &rva, &flags)) {
if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
// update the signal mask in preparation for signal handller
sigset_t blocksigs = __sighandmask[sig];
if (!(flags & SA_NODEFER))
blocksigs |= 1ull << (sig - 1);
ctx.uc_sigmask = atomic_fetch_or_explicit(&pt->tib->tib_sigmask, blocksigs,
memory_order_acquire);
// call the user's signal handler
char ssbuf[128];
siginfo_t si = {.si_signo = sig, .si_code = sic};
STRACE("__sig_raise(%G, %t) mask %s", sig, __sig_handler(rva),
_DescribeSigset(ssbuf, 0, (sigset_t *)&pt->tib->tib_sigmask));
__sig_handler(rva)(sig, &si, &ctx);
// record this handler
if (flags & SA_RESTART) {
handler_was_called |= SIG_HANDLED_SA_RESTART;
} else {
handler_was_called |= SIG_HANDLED_NO_RESTART;
}
}
// restore sigmask
// loop back to top
// jump where handler says
sig = 0;
return setcontext(&ctx);
}
textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) {
sigset_t m;
int handler_was_called;
m = atomic_exchange_explicit(&__get_tls()->tib_sigmask, waitmask,
memory_order_acquire);
handler_was_called = __sig_raise(sig, SI_KERNEL);
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
return handler_was_called;
}
// the user's signal handler callback is wrapped with this trampoline
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
int sig = sf->si.si_signo;
struct CosmoTib *tib = __get_tls();
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
for (;;) {
// update the signal mask in preparation for signal handler
sigset_t blocksigs = __sighandmask[sig];
if (!(sf->flags & SA_NODEFER))
blocksigs |= 1ull << (sig - 1);
sf->ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
memory_order_acquire);
// call the user's signal handler
char ssbuf[2][128];
STRACE("__sig_tramp(%G, %t) mask %s → %s", sig, __sig_handler(sf->rva),
_DescribeSigset(ssbuf[0], 0, &sf->ctx.uc_sigmask),
_DescribeSigset(ssbuf[1], 0, (sigset_t *)&tib->tib_sigmask));
__sig_handler(sf->rva)(sig, &sf->si, &sf->ctx);
// restore the signal mask that was used by the interrupted code
// this may have been modified by the signal handler in the callback
atomic_store_explicit(&tib->tib_sigmask, sf->ctx.uc_sigmask,
memory_order_release);
// jump back into original code if there aren't any pending signals
do {
if (!(sig = __sig_get(sf->ctx.uc_sigmask)))
__sig_restore(&sf->ctx);
} while (!__sig_start(pt, sig, &sf->rva, &sf->flags));
// tail recurse into another signal handler
sf->si.si_signo = sig;
sf->si.si_code = SI_KERNEL;
if (sf->flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
}
}
// sends signal to another specific thread which is ref'd
static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
unsigned rva = __sighandrvas[sig];
unsigned flags = __sighandflags[sig];
// do nothing if signal is ignored
if (rva == (intptr_t)SIG_IGN ||
(rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
STRACE("ignoring %G", sig);
return 0;
}
// we can't preempt threads that masked sigs or are blocked on i/o
while ((atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1)))) {
if (atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_acq_rel) &
(1ull << (sig - 1)))
// we believe signal was already enqueued
return 0;
if (__sig_wake(pt, sig))
// we believe i/o routine will handle signal
return 0;
if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1)))
// we believe ALLOW_SIGNALS will handle signal
return 0;
if (!(atomic_fetch_and_explicit(&pt->tib->tib_sigpending,
~(1ull << (sig - 1)),
memory_order_acq_rel) &
(1ull << (sig - 1))))
// we believe another thread sniped our signal
return 0;
break;
}
// avoid race conditions and deadlocks with thread suspend process
if (atomic_exchange_explicit(&pt->pt_intoff, 1, memory_order_acquire)) {
// we believe another thread is asynchronously waking the mark
if (atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_acq_rel) &
(1ull << (sig - 1)))
// we believe our signal is already being delivered
return 0;
if (atomic_load_explicit(&pt->pt_intoff, memory_order_acquire) ||
atomic_exchange_explicit(&pt->pt_intoff, 1, memory_order_acquire))
// we believe __sig_tramp will deliver our signal
return 0;
if (!(atomic_fetch_and_explicit(&pt->tib->tib_sigpending,
~(1ull << (sig - 1)),
memory_order_acq_rel) &
(1ull << (sig - 1))))
// we believe another thread sniped our signal
return 0;
}
// if there's no handler then killing a thread kills the process
if (rva == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
// take control of thread
// suspending the thread happens asynchronously
// however getting the context blocks until it's frozen
uintptr_t th = _pthread_syshand(pt);
if (SuspendThread(th) == -1u) {
STRACE("SuspendThread failed w/ %d", GetLastError());
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
return ESRCH;
}
struct NtContext nc;
nc.ContextFlags = kNtContextFull;
if (!GetThreadContext(th, &nc)) {
STRACE("GetThreadContext failed w/ %d", GetLastError());
ResumeThread(th);
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
return ESRCH;
}
// we can't preempt threads that masked sig or are blocked
// we can't preempt threads that are running in win32 code
// so we shall unblock the thread and let it signal itself
if (!((uintptr_t)__executable_start <= nc.Rip &&
nc.Rip < (uintptr_t)__privileged_start)) {
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_relaxed);
ResumeThread(th);
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
__sig_wake(pt, sig);
return 0;
}
// preferring to live dangerously
// the thread will be signaled asynchronously
if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
// inject call to trampoline function into thread
uintptr_t sp;
if (__sig_should_use_altstack(flags, pt->tib)) {
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
} else {
sp = nc.Rsp;
}
sp -= sizeof(struct SignalFrame);
sp &= -16;
struct SignalFrame *sf = (struct SignalFrame *)sp;
_ntcontext2linux(&sf->ctx, &nc);
bzero(&sf->si, sizeof(sf->si));
sf->rva = rva;
sf->flags = flags;
sf->si.si_code = sic;
sf->si.si_signo = sig;
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = nc.Rip;
nc.Rip = (intptr_t)__sig_tramp;
nc.Rdi = (intptr_t)sf;
nc.Rsp = sp;
if (!SetThreadContext(th, &nc)) {
STRACE("SetThreadContext failed w/ %d", GetLastError());
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
return ESRCH;
}
ResumeThread(th);
__sig_wake(pt, sig);
return 0;
}
// sends signal to another specific thread
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
int rc;
BLOCK_SIGNALS;
rc = __sig_killer(pt, sig, sic);
ALLOW_SIGNALS;
return rc;
}
// sends signal to any other thread
// this should only be called by non-posix threads
textwindows void __sig_generate(int sig, int sic) {
struct Dll *e;
struct PosixThread *pt, *mark = 0;
if (__sig_ignored(sig)) {
STRACE("ignoring %G", sig);
return;
}
if (__sighandrvas[sig] == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
if (atomic_load_explicit(__sig.process, memory_order_acquire) &
(1ull << (sig - 1))) {
return;
}
_pthread_lock();
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
pt = POSIXTHREAD_CONTAINER(e);
// we don't want to signal ourself
if (pt == _pthread_self())
continue;
// we don't want to signal a thread that isn't running
if (atomic_load_explicit(&pt->pt_status, memory_order_acquire) >=
kPosixThreadTerminated) {
continue;
}
// choose this thread if it isn't masking sig
if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1)))) {
_pthread_ref(pt);
mark = pt;
break;
}
// if a thread is blocking then we check to see if it's planning
// to unblock our sig once the wait operation is completed; when
// that's the case we can cancel the thread's i/o to deliver sig
if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) &&
!(pt->pt_blkmask & (1ull << (sig - 1)))) {
_pthread_ref(pt);
mark = pt;
break;
}
}
_pthread_unlock();
if (mark) {
// no lock needed since current thread is nameless and formless
__sig_killer(mark, sig, sic);
_pthread_unref(mark);
} else {
atomic_fetch_or_explicit(__sig.process, 1ull << (sig - 1),
memory_order_relaxed);
}
}
static textwindows char *__sig_stpcpy(char *d, const char *s) {
size_t i;
for (i = 0;; ++i)
if (!(d[i] = s[i]))
return d + i;
}
static textwindows wontreturn void __sig_death(int sig, const char *thing) {
#ifndef TINY
intptr_t hStderr;
char sigbuf[21], s[128], *p;
hStderr = GetStdHandle(kNtStdErrorHandle);
p = __sig_stpcpy(s, "Terminating on ");
p = __sig_stpcpy(p, thing);
p = __sig_stpcpy(p, strsignal_r(sig, sigbuf));
p = __sig_stpcpy(p,
". Pass --strace and/or ShowCrashReports() for details.\n");
WriteFile(hStderr, s, p - s, 0, 0);
#endif
__sig_terminate(sig);
}
static textwindows void __sig_unmaskable(struct NtExceptionPointers *ep,
int code, int sig,
struct CosmoTib *tib) {
// log vital crash information reliably for --strace before doing much
// we don't print this without the flag since raw numbers scare people
// this needs at least one page of stack memory in order to get logged
// otherwise it'll print a warning message about the lack of stack mem
STRACE("win32 vectored exception 0x%08Xu raising %G "
"cosmoaddr2line %s %lx %s",
ep->ExceptionRecord->ExceptionCode, sig,
_weaken(FindDebugBinary) ? _weaken(FindDebugBinary)()
: program_invocation_name,
ep->ContextRecord->Rip,
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
// if the user didn't install a signal handler for this unmaskable
// exception, then print a friendly helpful hint message to stderr
unsigned rva = __sighandrvas[sig];
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN)
__sig_death(sig, "uncaught ");
// if this signal handler is configured to auto-reset to the default
// then that reset needs to happen before the user handler is called
unsigned flags = __sighandflags[sig];
if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
// determine the true memory address at which fault occurred
// if this is a stack overflow then reapply guard protection
void *si_addr;
if (ep->ExceptionRecord->ExceptionCode == kNtSignalGuardPage) {
si_addr = (void *)ep->ExceptionRecord->ExceptionInformation[1];
} else {
si_addr = ep->ExceptionRecord->ExceptionAddress;
}
// call the user signal handler
// and a modifiable view of the faulting code's cpu state
// temporarily replace signal mask while calling crash handler
// abort process if sig is already blocked to avoid crash loop
// note ucontext_t is a hefty data structures on top of NtContext
ucontext_t ctx = {0};
siginfo_t si = {.si_signo = sig, .si_code = code, .si_addr = si_addr};
_ntcontext2linux(&ctx, ep->ContextRecord);
sigset_t blocksigs = __sighandmask[sig];
if (!(flags & SA_NODEFER))
blocksigs |= 1ull << (sig - 1);
ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
memory_order_acquire);
if (ctx.uc_sigmask & (1ull << (sig - 1))) {
__sig_death(sig, "masked ");
__sig_terminate(sig);
}
__sig_handler(rva)(sig, &si, &ctx);
atomic_store_explicit(&tib->tib_sigmask, ctx.uc_sigmask,
memory_order_release);
_ntlinux2context(ep->ContextRecord, &ctx);
}
void __stack_call(struct NtExceptionPointers *, int, int, struct CosmoTib *,
void (*)(struct NtExceptionPointers *, int, int,
struct CosmoTib *),
void *);
// abashed the devil stood
// and felt how awful goodness is
__msabi dontinstrument unsigned __sig_crash(struct NtExceptionPointers *ep) {
// translate win32 to unix si_signo and si_code
int code, sig = __sig_crash_sig(ep->ExceptionRecord->ExceptionCode, &code);
// advance the instruction pointer to skip over debugger breakpoints
// this behavior is consistent with how unix kernels are implemented
if (sig == SIGTRAP) {
ep->ContextRecord->Rip++;
if (__sig_ignored(sig))
return kNtExceptionContinueExecution;
}
// win32 stack overflow detection executes INSIDE the guard page
// thus switch to the alternate signal stack as soon as possible
struct CosmoTib *tib = __get_tls();
unsigned flags = __sighandflags[sig];
if (__sig_should_use_altstack(flags, tib)) {
__stack_call(ep, code, sig, tib, __sig_unmaskable,
tib->tib_sigstack_addr + tib->tib_sigstack_size);
} else {
__sig_unmaskable(ep, code, sig, tib);
}
// resume running user program
// hopefully the user fixed the cpu state
// otherwise the crash will keep happening
return kNtExceptionContinueExecution;
}
static textwindows int __sig_console_sig(uint32_t dwCtrlType) {
switch (dwCtrlType) {
case kNtCtrlCEvent:
return SIGINT;
case kNtCtrlBreakEvent:
return SIGQUIT;
case kNtCtrlCloseEvent:
case kNtCtrlLogoffEvent: // only received by services
case kNtCtrlShutdownEvent: // only received by services
return SIGHUP;
default:
return SIGSTKFLT;
}
}
__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
// win32 launches a thread to deliver ctrl-c and ctrl-break when typed
// it only happens when kNtEnableProcessedInput is in play on console.
// otherwise we need to wait until read-nt.c discovers that keystroke.
struct CosmoTib tls;
__bootstrap_tls(&tls, __builtin_frame_address(0));
__sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL);
return true;
}
// returns 0 if no signal handlers were called, otherwise a bitmask
// consisting of `1` which means a signal handler was invoked which
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
// handlers were called (or `3` if both were the case).
textwindows int __sig_check(void) {
int sig, res = 0;
while ((sig = __sig_get(atomic_load_explicit(&__get_tls()->tib_sigmask,
memory_order_acquire))))
res |= __sig_raise(sig, SI_KERNEL);
return res;
}
// background thread for delivering inter-process signals asynchronously
// this checks for undelivered process-wide signals, once per scheduling
// quantum, which on windows should be every ~15ms or so, unless somehow
// the process was tuned to have more fine-grained event timing. we want
// signals to happen faster when possible; that happens when cancelation
// points, e.g. read need to wait on i/o; they too check for new signals
textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
struct CosmoTib tls;
__bootstrap_tls(&tls, __builtin_frame_address(0));
char *sp = __builtin_frame_address(0);
__maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ,
STKSZ);
for (;;) {
// dequeue all pending signals and fire them off. if there's no
// thread that can handle them then __sig_generate will requeue
// those signals back to __sig.process; hence the need for xchg
unsigned long sigs =
atomic_exchange_explicit(__sig.process, 0, memory_order_acq_rel);
while (sigs) {
int sig = bsfl(sigs) + 1;
sigs &= ~(1ull << (sig - 1));
__sig_generate(sig, SI_KERNEL);
}
// unblock stalled asynchronous signals in threads
_pthread_lock();
for (struct Dll *e = dll_first(_pthread_list); e;
e = dll_next(_pthread_list, e)) {
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
if (atomic_load_explicit(&pt->pt_status, memory_order_acquire) >=
kPosixThreadTerminated) {
break;
}
sigset_t pending =
atomic_load_explicit(&pt->tib->tib_sigpending, memory_order_acquire);
sigset_t mask =
atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire);
if (pending & ~mask) {
_pthread_ref(pt);
_pthread_unlock();
while (!atomic_compare_exchange_weak_explicit(
&pt->tib->tib_sigpending, &pending, pending & ~mask,
memory_order_acq_rel, memory_order_relaxed)) {
}
while ((pending = pending & ~mask)) {
int sig = bsfl(pending) + 1;
pending &= ~(1ull << (sig - 1));
__sig_killer(pt, sig, SI_KERNEL);
}
_pthread_lock();
_pthread_unref(pt);
}
}
_pthread_unlock();
// wait until next scheduler quantum
Sleep(POLL_INTERVAL_MS);
}
return 0;
}
__attribute__((__constructor__(10))) textstartup void __sig_init(void) {
if (!IsWindows())
return;
AddVectoredExceptionHandler(true, (void *)__sig_crash);
SetConsoleCtrlHandler((void *)__sig_console, true);
CreateThread(0, STKSZ, __sig_worker, 0, kNtStackSizeParamIsAReservation, 0);
}
#endif /* __x86_64__ */

View file

@ -423,7 +423,7 @@ static int __sigaction(int sig, const struct sigaction *act,
* } * }
* *
* void ContinueOnCrash(void) { * void ContinueOnCrash(void) {
* struct sigaction sa = {.sa_sigaction = OnCrash, * struct sigaction sa = {.sa_handler = OnSigSegv,
* .sa_flags = SA_SIGINFO | SA_RESETHAND}; * .sa_flags = SA_SIGINFO | SA_RESETHAND};
* sigaction(SIGSEGV, &sa, 0); * sigaction(SIGSEGV, &sa, 0);
* sigaction(SIGFPE, &sa, 0); * sigaction(SIGFPE, &sa, 0);

View file

@ -113,7 +113,7 @@ static int sigaltstack_bsd(const struct sigaltstack *neu,
* struct sigaction sa; * struct sigaction sa;
* struct sigaltstack ss; * struct sigaltstack ss;
* ss.ss_flags = 0; * ss.ss_flags = 0;
* ss.ss_size = sysconf(_SC_SIGSTKSZ); * ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192;
* ss.ss_sp = malloc(ss.ss_size); * ss.ss_sp = malloc(ss.ss_size);
* sigaltstack(&ss, 0); * sigaltstack(&ss, 0);
* sigemptyset(&sa.ss_mask); * sigemptyset(&sa.ss_mask);
@ -121,16 +121,11 @@ static int sigaltstack_bsd(const struct sigaltstack *neu,
* sa.sa_handler = OnStackOverflow; * sa.sa_handler = OnStackOverflow;
* sigaction(SIGSEGV, &sa, 0); * sigaction(SIGSEGV, &sa, 0);
* *
* Your stack size should be `sysconf(_SC_SIGSTKSZ)` which should be
* somewhere in the ballpark of 32kb to 64kb. You should go no lower
* than `sysconf(_SC_MINSIGSTKSZ) + 2048` which could be 4kb - 34kb.
* Cosmo also defines `SIGSTKSZ` as 32kb, which should also be safe.
*
* @param neu if non-null will install new signal alt stack * @param neu if non-null will install new signal alt stack
* @param old if non-null will receive current signal alt stack * @param old if non-null will receive current signal alt stack
* @return 0 on success, or -1 w/ errno * @return 0 on success, or -1 w/ errno
* @raise EFAULT if bad memory was supplied * @raise EFAULT if bad memory was supplied
* @raise ENOMEM if `neu->ss_size` is beneath `sysconf(_SC_MINSIGSTKSZ)` * @raise ENOMEM if `neu->ss_size` is less than `MINSIGSTKSZ`
*/ */
int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) { int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
int rc; int rc;

View file

@ -33,7 +33,6 @@
#include "libc/runtime/syslib.internal.h" #include "libc/runtime/syslib.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
/** /**
* @fileoverview XNU kernel callback normalization. * @fileoverview XNU kernel callback normalization.
@ -514,7 +513,6 @@ privileged void __sigenter_xnu(int sig, struct siginfo_xnu *xnuinfo,
flags = __sighandflags[sig]; flags = __sighandflags[sig];
#ifdef __aarch64__ #ifdef __aarch64__
// xnu silicon claims to support sa_resethand but it does nothing // xnu silicon claims to support sa_resethand but it does nothing
// this can be tested, since it clears the bit from flags as well // this can be tested, since it clears the bit from flags as well
if (flags & SA_RESETHAND) { if (flags & SA_RESETHAND) {
@ -523,13 +521,6 @@ privileged void __sigenter_xnu(int sig, struct siginfo_xnu *xnuinfo,
__sighandflags[sig] = 0; __sighandflags[sig] = 0;
__sighandrvas[sig] = 0; __sighandrvas[sig] = 0;
} }
// unlike amd64, the instruction pointer on arm64 isn't advanced
// past the debugger breakpoint instruction automatically. we need
// this so execution can resume after __builtin_trap().
if (xnuctx && sig == SIGTRAP)
xnuctx->uc_mcontext->__ss.__pc += 4;
#endif #endif
if (~flags & SA_SIGINFO) { if (~flags & SA_SIGINFO) {

View file

@ -21,7 +21,6 @@
#include "libc/calls/sig.internal.h" #include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
@ -60,7 +59,8 @@ int sigsuspend(const sigset_t *ignore) {
// using SetEvent() whereas it takes ~30us to use SuspendThread(), // using SetEvent() whereas it takes ~30us to use SuspendThread(),
// GetThreadContext(), SetThreadContext(), and ResumeThread(). // GetThreadContext(), SetThreadContext(), and ResumeThread().
BLOCK_SIGNALS; BLOCK_SIGNALS;
rc = _park_norestart(timespec_max, waitmask); while (!(rc = _park_norestart(-1u, waitmask)))
donothing;
ALLOW_SIGNALS; ALLOW_SIGNALS;
} else { } else {
rc = sys_sigsuspend((uint64_t[2]){waitmask}, 8); rc = sys_sigsuspend((uint64_t[2]){waitmask}, 8);

View file

@ -13,6 +13,7 @@ extern unsigned __sighandflags[NSIG + 1];
extern uint64_t __sighandmask[NSIG + 1]; extern uint64_t __sighandmask[NSIG + 1];
extern const struct NtSecurityAttributes kNtIsInheritable; extern const struct NtSecurityAttributes kNtIsInheritable;
void __fds_wipe(void);
void __fds_lock(void); void __fds_lock(void);
void __fds_unlock(void); void __fds_unlock(void);

View file

@ -4,21 +4,19 @@ COSMOPOLITAN_C_START_
typedef uint64_t sigset_t; typedef uint64_t sigset_t;
/* clang-format off */ int sigaddset(sigset_t *, int) paramsnonnull();
int sigaddset(sigset_t *, int) libcesque paramsnonnull(); int sigdelset(sigset_t *, int) paramsnonnull();
int sigdelset(sigset_t *, int) libcesque paramsnonnull(); int sigemptyset(sigset_t *) paramsnonnull();
int sigemptyset(sigset_t *) libcesque paramsnonnull(); int sigfillset(sigset_t *) paramsnonnull();
int sigfillset(sigset_t *) libcesque paramsnonnull(); int sigandset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
int sigandset(sigset_t *, const sigset_t *, const sigset_t *) libcesque paramsnonnull(); int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
int sigorset(sigset_t *, const sigset_t *, const sigset_t *) libcesque paramsnonnull(); int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect;
int sigisemptyset(const sigset_t *) libcesque paramsnonnull() nosideeffect; int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
int sigismember(const sigset_t *, int) libcesque paramsnonnull() nosideeffect; int sigcountset(const sigset_t *) paramsnonnull() nosideeffect;
int sigcountset(const sigset_t *) libcesque paramsnonnull() nosideeffect; int sigprocmask(int, const sigset_t *, sigset_t *);
int sigprocmask(int, const sigset_t *, sigset_t *) dontthrow; int sigsuspend(const sigset_t *);
int sigsuspend(const sigset_t *) dontthrow; int sigpending(sigset_t *);
int sigpending(sigset_t *) libcesque; int pthread_sigmask(int, const sigset_t *, sigset_t *);
int pthread_sigmask(int, const sigset_t *, sigset_t *) dontthrow;
/* clang-format on */
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGSET_H_ */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGSET_H_ */

View file

@ -5,15 +5,27 @@
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#ifndef MODE_DBG
/* block sigs because theoretical edge cases */
#define BLOCK_SIGNALS \ #define BLOCK_SIGNALS \
do { \ do { \
sigset_t _SigMask; \ sigset_t _SigMask; \
_SigMask = __sig_block() _SigMask = __sig_block()
#define ALLOW_SIGNALS \ #define ALLOW_SIGNALS \
__sig_unblock(_SigMask); \ __sig_unblock(_SigMask); \
} \ } \
while (0) while (0)
#else
/* doesn't block signals so we can get a crash
report, when a core runtime library crashes */
#define BLOCK_SIGNALS \
do { \
sigset_t _SigMask; \
sigprocmask(SIG_SETMASK, 0, &_SigMask)
#define ALLOW_SIGNALS \
} \
while (0)
#endif
sigset_t __sig_block(void); sigset_t __sig_block(void);
void __sig_unblock(sigset_t); void __sig_unblock(sigset_t);

View file

@ -1,14 +1,13 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ #ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
#include "libc/nt/struct/context.h"
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#ifdef __x86_64__ #ifdef __x86_64__
#define PC rip #define PC rip
#define SP rsp #define SP rsp
#define BP rbp #define BP rbp
#define RES0 rax
#define RES1 rdx
#define ARG0 rdi #define ARG0 rdi
#define ARG1 rsi #define ARG1 rsi
#define ARG2 rdx #define ARG2 rdx
@ -19,8 +18,6 @@ COSMOPOLITAN_C_START_
#define PC pc #define PC pc
#define SP sp #define SP sp
#define BP regs[29] #define BP regs[29]
#define RES0 regs[0]
#define RES1 regs[1]
#define ARG0 regs[0] #define ARG0 regs[0]
#define ARG1 regs[1] #define ARG1 regs[1]
#define ARG2 regs[2] #define ARG2 regs[2]
@ -31,5 +28,8 @@ COSMOPOLITAN_C_START_
#error "unsupported architecture" #error "unsupported architecture"
#endif #endif
void _ntcontext2linux(struct ucontext *, const struct NtContext *);
void _ntlinux2context(struct NtContext *, const ucontext_t *);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ */

View file

@ -0,0 +1,71 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_
COSMOPOLITAN_C_START_
/**
* Linux Kernel user registers.
*
* @note superset of struct pt_regs
* @see ptrace() w/ PTRACE_SYSCALL
*/
struct user_regs_struct {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t rbp;
uint64_t rbx;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rax;
uint64_t rcx;
uint64_t rdx;
uint64_t rsi;
uint64_t rdi;
uint64_t orig_rax;
uint64_t rip;
uint64_t cs;
uint64_t eflags;
uint64_t rsp;
uint64_t ss;
uint64_t fs_base;
uint64_t gs_base;
uint64_t ds;
uint64_t es;
uint64_t fs;
uint64_t gs;
};
struct useregs_struct_freebsd {
int64_t r15;
int64_t r14;
int64_t r13;
int64_t r12;
int64_t r11;
int64_t r10;
int64_t r9;
int64_t r8;
int64_t rdi;
int64_t rsi;
int64_t rbp;
int64_t rbx;
int64_t rdx;
int64_t rcx;
int64_t rax;
uint32_t trapno;
uint16_t fs;
uint16_t gs;
uint32_t err;
uint16_t es;
uint16_t ds;
int64_t rip;
int64_t cs;
int64_t rflags;
int64_t rsp;
int64_t ss;
};
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_ */

View file

@ -31,17 +31,17 @@
// @returnstwice // @returnstwice
.ftrace1 .ftrace1
swapcontext: swapcontext:
beg
.ftrace2 .ftrace2
#include "libc/intrin/getcontext.inc" #include "libc/calls/getcontext.inc"
#ifdef __x86_64__ #ifdef __x86_64__
pro push %rbp
cpush %rsi mov %rsp,%rbp
cpush %rsi push %rsi
push %rsi
call __swapcontextsig call __swapcontextsig
cpop %rdi pop %rdi
cpop %rdi pop %rdi
epi pop %rbp
test %eax,%eax test %eax,%eax
jnz 1f jnz 1f
#elif defined(__aarch64__) #elif defined(__aarch64__)
@ -56,5 +56,4 @@ swapcontext:
#endif #endif
jmp __tailcontext jmp __tailcontext
1: ret 1: ret
end
.endfn swapcontext,globl .endfn swapcontext,globl

View file

@ -2,9 +2,6 @@
#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
extern int sys_getppid_nt_cosmo;
extern int sys_getppid_nt_win32;
bool32 sys_isatty(int); bool32 sys_isatty(int);
int sys_chdir_nt(const char *); int sys_chdir_nt(const char *);
int sys_dup_nt(int, int, int, int); int sys_dup_nt(int, int, int, int);
@ -40,7 +37,6 @@ int sys_unlinkat_nt(int, const char *, int);
int64_t sys_lseek_nt(int, int64_t, int); int64_t sys_lseek_nt(int, int64_t, int);
ssize_t sys_read_nt_impl(int, void *, size_t, int64_t); ssize_t sys_read_nt_impl(int, void *, size_t, int64_t);
ssize_t sys_readlinkat_nt(int, const char *, char *, size_t); ssize_t sys_readlinkat_nt(int, const char *, char *, size_t);
void sys_getppid_nt_wipe(int, int);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ */ #endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ */

View file

@ -57,7 +57,8 @@ __tailcontext:
mov 80(%rax),%rsp mov 80(%rax),%rsp
push 88(%rax) push 88(%rax)
mov 24(%rax),%rdi mov 24(%rax),%rdi
mov 64(%rax),%rax
xor %eax,%eax
ret ret
#elif defined(__aarch64__) #elif defined(__aarch64__)

View file

@ -16,9 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/time.h"
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h"
#include "libc/sysv/consts/clock.h" #include "libc/dce.h"
#include "libc/sysv/errfuns.h"
/** /**
* Returns time as seconds from UNIX epoch. * Returns time as seconds from UNIX epoch.
@ -28,11 +29,15 @@
* @asyncsignalsafe * @asyncsignalsafe
*/ */
int64_t time(int64_t *opt_out_ret) { int64_t time(int64_t *opt_out_ret) {
int64_t secs = -1; int64_t secs;
struct timespec ts; struct timeval tv;
if (!clock_gettime(CLOCK_REALTIME, &ts)) if (gettimeofday(&tv, 0) != -1) {
secs = ts.tv_sec; secs = tv.tv_sec;
if (opt_out_ret) if (opt_out_ret) {
*opt_out_ret = secs; *opt_out_ret = secs;
}
} else {
secs = -1;
}
return secs; return secs;
} }

View file

@ -23,7 +23,7 @@
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h" #include "libc/thread/tls.h"
int __tailcontext(const ucontext_t *) wontreturn; int __tailcontext(const ucontext_t *);
/** /**
* Sets machine context. * Sets machine context.
@ -40,7 +40,7 @@ int setcontext(const ucontext_t *uc) {
} else { } else {
sys_sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0); sys_sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0);
} }
__tailcontext(uc); return __tailcontext(uc);
} }
int __getcontextsig(ucontext_t *uc) { int __getcontextsig(ucontext_t *uc) {

View file

@ -34,14 +34,10 @@
* @norestart * @norestart
*/ */
int usleep(uint64_t micros) { int usleep(uint64_t micros) {
// All OSes except OpenBSD return instantly on usleep(0). So we might
// as well avoid system call overhead and helping OpenBSD work better
if (micros) {
errno_t err; errno_t err;
struct timespec ts = timespec_frommicros(micros); struct timespec ts = timespec_frommicros(micros);
err = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, 0); err = clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0);
if (err) if (err)
return errno = err, -1; return errno = err, -1;
}
return 0; return 0;
} }

View file

@ -80,8 +80,7 @@ textwindows int IsWindowsExecutable(int64_t handle, const char16_t *path) {
uint32_t got; uint32_t got;
BLOCK_SIGNALS; BLOCK_SIGNALS;
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0)}; struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0)};
ok = overlap.hEvent && ok = (ReadFile(handle, buf, 2, 0, &overlap) ||
(ReadFile(handle, buf, 2, 0, &overlap) ||
GetLastError() == kNtErrorIoPending) && GetLastError() == kNtErrorIoPending) &&
GetOverlappedResult(handle, &overlap, &got, true); GetOverlappedResult(handle, &overlap, &got, true);
CloseHandle(overlap.hEvent); CloseHandle(overlap.hEvent);

View file

@ -1,6 +1,5 @@
#ifndef COSMOPOLITAN_LIBC_COSMO_H_ #ifndef COSMOPOLITAN_LIBC_COSMO_H_
#define COSMOPOLITAN_LIBC_COSMO_H_ #define COSMOPOLITAN_LIBC_COSMO_H_
#include "libc/calls/struct/timespec.h"
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#ifndef __cplusplus #ifndef __cplusplus
@ -9,33 +8,14 @@ COSMOPOLITAN_C_START_
#define _COSMO_ATOMIC(x) x #define _COSMO_ATOMIC(x) x
#endif #endif
errno_t cosmo_once(_COSMO_ATOMIC(unsigned) *, void (*)(void)); errno_t cosmo_once(_COSMO_ATOMIC(unsigned) *, void (*)(void)) libcesque;
int systemvpe(const char *, char *const[], char *const[]) libcesque; int systemvpe(const char *, char *const[], char *const[]) libcesque;
char *GetProgramExecutableName(void) libcesque; char *GetProgramExecutableName(void) libcesque;
void unleaf(void) libcesque; void unleaf(void) libcesque;
bool32 IsLinuxModern(void) libcesque;
int __demangle(char *, const char *, size_t) libcesque; int __demangle(char *, const char *, size_t) libcesque;
int __is_mangled(const char *) libcesque; int __is_mangled(const char *) libcesque;
bool32 IsLinuxModern(void) libcesque;
int cosmo_args(const char *, char ***) libcesque;
int LoadZipArgs(int *, char ***) libcesque; int LoadZipArgs(int *, char ***) libcesque;
int cosmo_futex_wake(_COSMO_ATOMIC(int) *, int, char);
int cosmo_futex_wait(_COSMO_ATOMIC(int) *, int, char, int,
const struct timespec *);
errno_t cosmo_stack_alloc(size_t *, size_t *, void **) libcesque;
errno_t cosmo_stack_free(void *, size_t, size_t) libcesque;
void cosmo_stack_clear(void) libcesque;
void cosmo_stack_setmaxstacks(int) libcesque;
int cosmo_stack_getmaxstacks(void) libcesque;
int __deadlock_check(void *, int) libcesque;
int __deadlock_tracked(void *) libcesque;
void __deadlock_record(void *, int) libcesque;
void __deadlock_track(void *, int) libcesque;
void __deadlock_untrack(void *) libcesque;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_COSMO_H_ */ #endif /* COSMOPOLITAN_LIBC_COSMO_H_ */

View file

@ -47,14 +47,7 @@ __oops_win32:
// @note ape.S and ape-loader both set RCX to XNU on Darwin // @note ape.S and ape-loader both set RCX to XNU on Darwin
// @noreturn // @noreturn
_start: _start:
.cfi_startproc #ifdef __x86_64__
#if defined(__x86_64__)
.cfi_undefined rip
#elif defined(__aarch64__)
.cfi_undefined x30
#endif /* __x86_64__ */
#if defined(__x86_64__)
#if SupportsFreebsd() #if SupportsFreebsd()
// detect free besiyata dishmaya // detect free besiyata dishmaya
@ -166,5 +159,4 @@ _start:
#else #else
#error "architecture unsupported" #error "architecture unsupported"
#endif /* __x86_64__ */ #endif /* __x86_64__ */
.cfi_endproc
.endfn _start,weak,hidden .endfn _start,weak,hidden

View file

@ -57,7 +57,6 @@
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/temp.h" #include "libc/temp.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/thread/tls.h" #include "libc/thread/tls.h"
@ -132,8 +131,6 @@ struct {
long __sysv2nt14(); long __sysv2nt14();
long foreign_tramp(); long foreign_tramp();
void __dlopen_lock(void);
void __dlopen_unlock(void);
static _Thread_local char dlerror_buf[128]; static _Thread_local char dlerror_buf[128];
@ -254,7 +251,7 @@ static bool elf_slurp(struct Loaded *l, int fd, const char *file) {
return true; return true;
} }
dontinline static bool elf_load(struct Loaded *l, const char *file, long pagesz, static dontinline bool elf_load(struct Loaded *l, const char *file, long pagesz,
char *interp_path, size_t interp_size) { char *interp_path, size_t interp_size) {
int fd; int fd;
if ((fd = open(file, O_RDONLY | O_CLOEXEC)) == -1) if ((fd = open(file, O_RDONLY | O_CLOEXEC)) == -1)
@ -280,7 +277,7 @@ static long *push_strs(long *sp, char **list, int count) {
return sp; return sp;
} }
wontreturn dontinstrument static void foreign_helper(void **p) { static wontreturn dontinstrument void foreign_helper(void **p) {
__foreign.dlopen = p[0]; __foreign.dlopen = p[0];
__foreign.dlsym = p[1]; __foreign.dlsym = p[1];
__foreign.dlclose = p[2]; __foreign.dlclose = p[2];
@ -288,7 +285,7 @@ wontreturn dontinstrument static void foreign_helper(void **p) {
_longjmp(__foreign.jb, 1); _longjmp(__foreign.jb, 1);
} }
dontinline static void elf_exec(const char *file, char **envp) { static dontinline void elf_exec(const char *file, char **envp) {
// get microprocessor page size // get microprocessor page size
long pagesz = __pagesize; long pagesz = __pagesize;
@ -412,7 +409,7 @@ static char *dlerror_set(const char *str) {
return dlerror_buf; return dlerror_buf;
} }
dontinline static char *foreign_alloc_block(void) { static dontinline char *foreign_alloc_block(void) {
char *p = 0; char *p = 0;
size_t sz = 65536; size_t sz = 65536;
if (!IsWindows()) { if (!IsWindows()) {
@ -435,16 +432,17 @@ dontinline static char *foreign_alloc_block(void) {
return p; return p;
} }
dontinline static void *foreign_alloc(size_t n) { static dontinline void *foreign_alloc(size_t n) {
void *res; void *res;
static char *block; static char *block;
__dlopen_lock(); static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&lock);
if (!block || READ32LE(block) + n > 65536) if (!block || READ32LE(block) + n > 65536)
if (!(block = foreign_alloc_block())) if (!(block = foreign_alloc_block()))
return 0; return 0;
res = block + READ32LE(block); res = block + READ32LE(block);
WRITE32LE(block, READ32LE(block) + n); WRITE32LE(block, READ32LE(block) + n);
__dlopen_unlock(); pthread_mutex_unlock(&lock);
return res; return res;
} }
@ -548,7 +546,7 @@ static void *foreign_thunk_nt(void *func) {
return code; return code;
} }
dontinline static bool foreign_compile(char exe[hasatleast PATH_MAX]) { static dontinline bool foreign_compile(char exe[hasatleast PATH_MAX]) {
// construct path // construct path
strlcpy(exe, get_tmp_dir(), PATH_MAX); strlcpy(exe, get_tmp_dir(), PATH_MAX);
@ -810,7 +808,7 @@ void *cosmo_dlopen(const char *path, int mode) {
} }
ALLOW_CANCELATION; ALLOW_CANCELATION;
ALLOW_SIGNALS; ALLOW_SIGNALS;
STRACE("cosmo_dlopen(%#s, %d) → %p% m", path, mode, res); STRACE("dlopen(%#s, %d) → %p% m", path, mode, res);
return res; return res;
} }
@ -855,7 +853,7 @@ void *cosmo_dlsym(void *handle, const char *name) {
} else { } else {
func = 0; func = 0;
} }
STRACE("cosmo_dlsym(%p, %#s) → %p", handle, name, func); STRACE("dlsym(%p, %#s) → %p", handle, name, func);
return func; return func;
} }
@ -890,7 +888,7 @@ int cosmo_dlclose(void *handle) {
} else { } else {
res = -1; res = -1;
} }
STRACE("cosmo_dlclose(%p) → %d", handle, res); STRACE("dlclose(%p) → %d", handle, res);
return res; return res;
} }
@ -909,6 +907,6 @@ char *cosmo_dlerror(void) {
} else { } else {
res = dlerror_buf; res = dlerror_buf;
} }
STRACE("cosmo_dlerror() → %#s", res); STRACE("dlerror() → %#s", res);
return res; return res;
} }

View file

@ -17,10 +17,6 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dlopen/dlfcn.h" #include "libc/dlopen/dlfcn.h"
#include "libc/intrin/strace.h"
#define DLOPEN_ERROR \
"dlopen() isn't supported; consider using cosmo_dlopen() and read its docs"
/** /**
* Opens dynamic shared object using host platform libc. * Opens dynamic shared object using host platform libc.
@ -31,13 +27,12 @@
* *
* @return null always * @return null always
*/ */
void *dlopen(const char *path, int mode) { void *dlopen(const char *, int) {
STRACE("dlopen(%#s, %d) → 0 [%s]", path, mode, DLOPEN_ERROR);
return 0; return 0;
} }
char *dlerror(void) { char *dlerror(void) {
return DLOPEN_ERROR; return "dlopen() isn't supported by cosmo; try using cosmo_dlopen()";
} }
void *dlsym(void *, const char *) { void *dlsym(void *, const char *) {

View file

@ -68,7 +68,6 @@
#define EM_NONE 0 #define EM_NONE 0
#define EM_M32 1 #define EM_M32 1
#define EM_386 3 #define EM_386 3
#define EM_MIPS 8
#define EM_PPC64 21 #define EM_PPC64 21
#define EM_S390 22 #define EM_S390 22
#define EM_ARM 40 #define EM_ARM 40

View file

@ -29,7 +29,7 @@ COSMOPOLITAN_C_START_
#define errno \ #define errno \
(*__extension__({ \ (*__extension__({ \
errno_t *__ep; \ errno_t *__ep; \
__asm__("sub\t%0,x28,#1024-0x3c" : "=r"(__ep)); \ __asm__("sub\t%0,x28,#512-0x3c" : "=r"(__ep)); \
__ep; \ __ep; \
})) }))
#else #else

Some files were not shown because too many files have changed in this diff Show more