mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-04 00:40:27 +00:00
Compare commits
No commits in common. "master" and "3.9.4" have entirely different histories.
583 changed files with 7518 additions and 13927 deletions
42
.github/workflows/build.yml
vendored
42
.github/workflows/build.yml
vendored
|
@ -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
1
.gitignore
vendored
|
@ -15,4 +15,3 @@ __pycache__
|
||||||
/tool/emacs/*.elc
|
/tool/emacs/*.elc
|
||||||
/perf.data
|
/perf.data
|
||||||
/perf.data.old
|
/perf.data.old
|
||||||
/qemu*core
|
|
||||||
|
|
19
Makefile
19
Makefile
|
@ -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 \
|
||||||
|
|
31
README.md
31
README.md
|
@ -3,12 +3,12 @@
|
||||||
[](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
|
[](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 |
|
||||||
|
|
||||||
|
|
|
@ -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)) }
|
||||||
|
|
||||||
|
|
26
ape/ape.lds
26
ape/ape.lds
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))))
|
||||||
|
|
353
examples/art.c
353
examples/art.c
|
@ -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);
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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));
|
||||||
|
|
322
examples/rote.c
322
examples/rote.c
|
@ -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");
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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 $<
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
|
@ -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])) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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, ×pec_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__ */
|
||||||
|
|
|
@ -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__ */
|
||||||
|
|
|
@ -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[] = {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
717
libc/calls/sig.c
Normal 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__ */
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
71
libc/calls/struct/user_regs_struct.h
Normal file
71
libc/calls/struct/user_regs_struct.h
Normal 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_ */
|
|
@ -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
|
|
@ -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_ */
|
||||||
|
|
|
@ -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__)
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
24
libc/cosmo.h
24
libc/cosmo.h
|
@ -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_ */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 *) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue