mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-02 16:00:30 +00:00
Compare commits
98 commits
Author | SHA1 | Date | |
---|---|---|---|
|
f1e83d5240 | ||
|
2fe8338f92 | ||
|
4ca513cba2 | ||
|
455910e8f2 | ||
|
9c68bc19b5 | ||
|
66d1050af6 | ||
|
fbc4fcbb71 | ||
|
afc986f741 | ||
|
5eb7cd6643 | ||
|
a8ed4fdd09 | ||
|
7b69652854 | ||
|
b235492e71 | ||
|
fc81fd8d16 | ||
|
38930de8e0 | ||
|
0e557d041d | ||
|
1d676b36e6 | ||
|
10a92cee94 | ||
|
42a9ed0131 | ||
|
12cb0669fb | ||
|
7f6a7d6fff | ||
|
9f6bf6ea71 | ||
|
102edf4ea2 | ||
|
21968acf99 | ||
|
98861b23fc | ||
|
dab6d7a345 | ||
|
90119c422c | ||
|
5907304049 | ||
|
035b0e2a62 | ||
|
7b67b20dae | ||
|
f0b0f926bf | ||
|
29eb7e67bb | ||
|
f71f61cd40 | ||
|
53c6edfd18 | ||
|
42a3bb729a | ||
|
c97a858470 | ||
|
4acd12a514 | ||
|
b734eec836 | ||
|
fe01642a20 | ||
|
e939659b70 | ||
|
ed6d133a27 | ||
|
97fc2aab41 | ||
|
662e7b217f | ||
|
27f2777cc6 | ||
|
538ce338f4 | ||
|
a15958edc6 | ||
|
8db646f6b2 | ||
|
fde03f8487 | ||
|
f24c854b28 | ||
|
0b3c81dd4e | ||
|
98c5847727 | ||
|
fd7da586b5 | ||
|
a51ccc8fb1 | ||
|
c7e3d9f7ff | ||
|
9ba5b227d9 | ||
|
aca4214ff6 | ||
|
379cd77078 | ||
|
36e5861b0c | ||
|
cc8a9eb93c | ||
|
0158579493 | ||
|
2de3845b25 | ||
|
93e22c581f | ||
|
ec2db4e40e | ||
|
55b7aa1632 | ||
|
4705705548 | ||
|
c8e10eef30 | ||
|
624573207e | ||
|
906bd06a5a | ||
|
c8c81af0c7 | ||
|
af7bd80430 | ||
|
26c051c297 | ||
|
9cc1bd04b2 | ||
|
69402f4d78 | ||
|
838b54f906 | ||
|
2d43d400c6 | ||
|
c22b413ac4 | ||
|
22094ae9ca | ||
|
bda2a4d55e | ||
|
b490e23d63 | ||
|
b40140e6c5 | ||
|
3142758675 | ||
|
cf9252f429 | ||
|
5fae582e82 | ||
|
ef00a7d0c2 | ||
|
746660066f | ||
|
fd15b2d7a3 | ||
|
e228aa3e14 | ||
|
9ddbfd921e | ||
|
729f7045e3 | ||
|
e47d67ba9b | ||
|
2477677c85 | ||
|
abdf6c9c26 | ||
|
5c3f854acb | ||
|
ad0a7c67c4 | ||
|
1312f60245 | ||
|
cafdb456ed | ||
|
4e9566cd33 | ||
|
5ce5fb6f2a | ||
|
d3279d3c0d |
542 changed files with 12628 additions and 7498 deletions
42
.github/workflows/build.yml
vendored
42
.github/workflows/build.yml
vendored
|
@ -1,5 +1,8 @@
|
||||||
name: build
|
name: build
|
||||||
|
|
||||||
|
env:
|
||||||
|
COSMOCC_VERSION: 3.9.2
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
|
@ -19,13 +22,48 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
mode: ["", tiny, rel, tinylinux, optlinux]
|
mode: ["", tiny, rel, tinylinux, optlinux]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
# Full checkout needed for git-restore-mtime-bare.
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# TODO(jart): fork this action.
|
||||||
|
- uses: chetan/git-restore-mtime-action@v2
|
||||||
|
|
||||||
|
- uses: actions/cache/restore@v4
|
||||||
|
id: cache
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
.cosmocc
|
||||||
|
o
|
||||||
|
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-
|
||||||
|
${{ env.COSMOCC_VERSION }}-
|
||||||
|
|
||||||
|
- name: Restore mtimes
|
||||||
|
if: steps.cache.outputs.cache-hit == 'true'
|
||||||
|
run: |
|
||||||
|
while read mtime file; do
|
||||||
|
[ -f "$file" ] && touch -d "@$mtime" "$file"
|
||||||
|
done < o/.mtimes
|
||||||
|
|
||||||
- name: support ape bins 1
|
- name: support ape bins 1
|
||||||
run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
|
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape
|
||||||
|
|
||||||
- name: support ape bins 2
|
- 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,3 +15,4 @@ __pycache__
|
||||||
/tool/emacs/*.elc
|
/tool/emacs/*.elc
|
||||||
/perf.data
|
/perf.data
|
||||||
/perf.data.old
|
/perf.data.old
|
||||||
|
/qemu*core
|
||||||
|
|
20
Makefile
20
Makefile
|
@ -77,7 +77,8 @@ 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 build/bootstrap/make, in which case make's working directory is in wsl
|
# - user ran .cosmocc/current/bin/make, in which case make's working directory
|
||||||
|
# is in wsl
|
||||||
# - user ran make, in which case cocmd's working directory is in wsl
|
# - 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)
|
||||||
|
@ -89,7 +90,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 build/bootstrap/make)
|
$(error please use https://cosmo.zip/pub/cosmos/bin/make)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LC_ALL = C
|
LC_ALL = C
|
||||||
|
@ -135,7 +136,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
|
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10 luna
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ZIPOBJ_FLAGS += -a$(ARCH)
|
ZIPOBJ_FLAGS += -a$(ARCH)
|
||||||
|
@ -274,6 +275,10 @@ 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 # │
|
||||||
|
@ -293,8 +298,7 @@ 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
|
||||||
|
@ -313,8 +317,6 @@ 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
|
||||||
|
@ -367,6 +369,7 @@ 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
|
||||||
|
@ -449,7 +452,6 @@ COSMOPOLITAN = \
|
||||||
LIBC_NT_BCRYPTPRIMITIVES \
|
LIBC_NT_BCRYPTPRIMITIVES \
|
||||||
LIBC_NT_COMDLG32 \
|
LIBC_NT_COMDLG32 \
|
||||||
LIBC_NT_GDI32 \
|
LIBC_NT_GDI32 \
|
||||||
LIBC_NT_SHELL32 \
|
|
||||||
LIBC_NT_IPHLPAPI \
|
LIBC_NT_IPHLPAPI \
|
||||||
LIBC_NT_KERNEL32 \
|
LIBC_NT_KERNEL32 \
|
||||||
LIBC_NT_NTDLL \
|
LIBC_NT_NTDLL \
|
||||||
|
@ -457,6 +459,7 @@ 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 \
|
||||||
|
@ -465,6 +468,7 @@ 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 \
|
||||||
|
|
25
README.md
25
README.md
|
@ -3,7 +3,7 @@
|
||||||
[](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
|
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++
|
||||||
a build-once run-anywhere language, like Java, except it doesn't need an
|
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
|
||||||
|
@ -87,15 +87,22 @@ 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
|
||||||
make life easier, we've included one in the cosmocc toolchain, which is
|
bootstrap your build, you can install Cosmopolitan Make from this site:
|
||||||
guaranteed to be compatible and furthermore includes our extensions for
|
|
||||||
doing build system sandboxing.
|
https://cosmo.zip/pub/cosmos/bin/make
|
||||||
|
|
||||||
|
E.g.:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
build/bootstrap/make -j8
|
curl -LO https://cosmo.zip/pub/cosmos/bin/make
|
||||||
|
./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
|
||||||
|
@ -103,7 +110,7 @@ depends on core LIBC packages.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
rm -rf o//libc o//test
|
rm -rf o//libc o//test
|
||||||
build/bootstrap/make o//test/posix/signal_test
|
.cosmocc/current/bin/make o//test/posix/signal_test
|
||||||
o//test/posix/signal_test
|
o//test/posix/signal_test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -112,21 +119,21 @@ list out each individual one. For example if you wanted to build and run
|
||||||
all the unit tests in the `TEST_POSIX` package, you could say:
|
all the unit tests in the `TEST_POSIX` package, you could say:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
build/bootstrap/make o//test/posix
|
.cosmocc/current/bin/make o//test/posix
|
||||||
```
|
```
|
||||||
|
|
||||||
Cosmopolitan provides a variety of build modes. For example, if you want
|
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
|
||||||
build/bootstrap/make m=tiny
|
.cosmocc/current/bin/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
|
||||||
build/bootstrap/make m=tinylinux
|
.cosmocc/current/bin/make m=tinylinux
|
||||||
```
|
```
|
||||||
|
|
||||||
For further details, see [//build/config.mk](build/config.mk).
|
For further details, see [//build/config.mk](build/config.mk).
|
||||||
|
|
|
@ -259,6 +259,9 @@ 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,6 +386,13 @@ 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()
|
||||||
|
@ -426,11 +433,6 @@ 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.*)))
|
||||||
|
@ -439,7 +441,6 @@ 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 = .;
|
||||||
|
@ -519,6 +520,9 @@ 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) }
|
||||||
|
|
||||||
|
@ -582,11 +586,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(.data);
|
ape_ram_vaddr = ADDR(.eh_frame);
|
||||||
ape_ram_offset = ape_ram_vaddr - __executable_start;
|
ape_ram_offset = ape_ram_vaddr - __executable_start;
|
||||||
ape_ram_paddr = LOADADDR(.data);
|
ape_ram_paddr = LOADADDR(.eh_frame);
|
||||||
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
|
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame);
|
||||||
ape_ram_memsz = _end - ADDR(.data);
|
ape_ram_memsz = _end - ADDR(.eh_frame);
|
||||||
ape_ram_align = CONSTANT(COMMONPAGESIZE);
|
ape_ram_align = CONSTANT(COMMONPAGESIZE);
|
||||||
ape_ram_rva = RVA(ape_ram_vaddr);
|
ape_ram_rva = RVA(ape_ram_vaddr);
|
||||||
|
|
||||||
|
@ -596,7 +600,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 : 8 * 1024 * 1024;
|
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 4 * 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 build/bootstrap/make ]; then
|
if [ -x .cosmocc/current/bin/make ]; then
|
||||||
MAKE=build/bootstrap/make
|
MAKE=.cosmocc/current/bin/make
|
||||||
else
|
else
|
||||||
MAKE=make
|
MAKE=make
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -99,3 +99,8 @@ 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 shared + 1;
|
return __atomic_load_n(&shared, __ATOMIC_RELAXED) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t weak_count() const noexcept
|
size_t weak_count() const noexcept
|
||||||
{
|
{
|
||||||
return weak;
|
return __atomic_load_n(&weak, __ATOMIC_RELAXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 !r.owner_before(*this);
|
return rc < r.rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -382,6 +382,34 @@ 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)
|
||||||
|
@ -410,6 +438,19 @@ 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())
|
||||||
|
|
|
@ -54,8 +54,8 @@ EXAMPLES_DIRECTDEPS = \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
LIBC_NT_ADVAPI32 \
|
LIBC_NT_ADVAPI32 \
|
||||||
LIBC_NT_IPHLPAPI \
|
LIBC_NT_IPHLPAPI \
|
||||||
LIBC_NT_MEMORY \
|
|
||||||
LIBC_NT_KERNEL32 \
|
LIBC_NT_KERNEL32 \
|
||||||
|
LIBC_NT_MEMORY \
|
||||||
LIBC_NT_NTDLL \
|
LIBC_NT_NTDLL \
|
||||||
LIBC_NT_USER32 \
|
LIBC_NT_USER32 \
|
||||||
LIBC_NT_WS2_32 \
|
LIBC_NT_WS2_32 \
|
||||||
|
@ -64,6 +64,7 @@ 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 \
|
||||||
|
@ -81,6 +82,8 @@ 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 \
|
||||||
|
@ -94,12 +97,10 @@ 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/asteroids.c
Normal file
353
examples/asteroids.c
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
// -*- mode:c; indent-tabs-mode:nil; c-basic-offset:4 -*-
|
||||||
|
// vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8
|
||||||
|
|
||||||
|
// asteroids by tsotchke
|
||||||
|
// https://github.com/tsotchke/asteroids
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 80
|
||||||
|
#define SCREEN_HEIGHT 24
|
||||||
|
#define MAX_ASTEROIDS 5
|
||||||
|
#define MAX_BULLETS 5
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x, y;
|
||||||
|
} Vector2;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vector2 position;
|
||||||
|
Vector2 velocity;
|
||||||
|
float angle;
|
||||||
|
float radius;
|
||||||
|
} GameObject;
|
||||||
|
|
||||||
|
GameObject spaceship;
|
||||||
|
GameObject asteroids[MAX_ASTEROIDS];
|
||||||
|
GameObject bullets[MAX_BULLETS];
|
||||||
|
|
||||||
|
int score = 0;
|
||||||
|
time_t startTime;
|
||||||
|
int isGameOver = 0;
|
||||||
|
int shouldExit = 0;
|
||||||
|
int finalTime = 0; // To store final time at game over
|
||||||
|
char display[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
|
// Function to clear the screen buffer
|
||||||
|
void clearDisplay() {
|
||||||
|
memset(display, ' ', sizeof(display));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to draw a pixel on the screen
|
||||||
|
void drawPixel(int x, int y) {
|
||||||
|
if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) {
|
||||||
|
display[y][x] = '*';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to draw a line using Bresenham's algorithm
|
||||||
|
void drawLine(int x1, int y1, int x2, int y2) {
|
||||||
|
int dx = abs(x2 - x1), sx = (x1 < x2) ? 1 : -1;
|
||||||
|
int dy = -abs(y2 - y1), sy = (y1 < y2) ? 1 : -1;
|
||||||
|
int error = dx + dy, e2;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
drawPixel(x1, y1);
|
||||||
|
if (x1 == x2 && y1 == y2) break;
|
||||||
|
e2 = 2 * error;
|
||||||
|
if (e2 >= dy) { error += dy; x1 += sx; }
|
||||||
|
if (e2 <= dx) { error += dx; y1 += sy; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to draw a circle
|
||||||
|
void drawCircle(int centerX, int centerY, int radius) {
|
||||||
|
int x = radius - 1, y = 0, dx = 1, dy = 1, err = dx - (radius << 1);
|
||||||
|
while (x >= y) {
|
||||||
|
drawPixel(centerX + x, centerY + y);
|
||||||
|
drawPixel(centerX + y, centerY + x);
|
||||||
|
drawPixel(centerX - y, centerY + x);
|
||||||
|
drawPixel(centerX - x, centerY + y);
|
||||||
|
drawPixel(centerX - x, centerY - y);
|
||||||
|
drawPixel(centerX - y, centerY - x);
|
||||||
|
drawPixel(centerX + y, centerY - x);
|
||||||
|
drawPixel(centerX + x, centerY - y);
|
||||||
|
|
||||||
|
if (err <= 0) {
|
||||||
|
y++;
|
||||||
|
err += dy;
|
||||||
|
dy += 2;
|
||||||
|
}
|
||||||
|
if (err > 0) {
|
||||||
|
x--;
|
||||||
|
dx += 2;
|
||||||
|
err += dx - (radius << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a game object
|
||||||
|
void initializeGameObject(GameObject *obj, float x, float y, float angle, float radius) {
|
||||||
|
obj->position = (Vector2){x, y};
|
||||||
|
obj->velocity = (Vector2){0, 0};
|
||||||
|
obj->angle = angle;
|
||||||
|
obj->radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap position of the spaceship and asteroids within screen bounds
|
||||||
|
void wrapPosition(Vector2 *pos) {
|
||||||
|
if (pos->x < 0) pos->x = SCREEN_WIDTH - 1;
|
||||||
|
if (pos->x >= SCREEN_WIDTH) pos->x = 0;
|
||||||
|
if (pos->y < 0) pos->y = SCREEN_HEIGHT - 1;
|
||||||
|
if (pos->y >= SCREEN_HEIGHT) pos->y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if two game objects are colliding
|
||||||
|
int checkCollision(GameObject *a, GameObject *b) {
|
||||||
|
float deltaX = a->position.x - b->position.x;
|
||||||
|
float deltaY = a->position.y - b->position.y;
|
||||||
|
return sqrt(deltaX * deltaX + deltaY * deltaY) < (a->radius + b->radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize game state
|
||||||
|
void initGame() {
|
||||||
|
score = 0; // Reset the score
|
||||||
|
initializeGameObject(&spaceship, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0, 2);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ASTEROIDS; i++) {
|
||||||
|
initializeGameObject(&asteroids[i],
|
||||||
|
rand() % SCREEN_WIDTH,
|
||||||
|
rand() % SCREEN_HEIGHT,
|
||||||
|
0,
|
||||||
|
2 + rand() % 3);
|
||||||
|
asteroids[i].velocity.x = ((float)rand() / RAND_MAX) * 2 - 1;
|
||||||
|
asteroids[i].velocity.y = ((float)rand() / RAND_MAX) * 2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||||
|
bullets[i].position.x = -1; // Mark bullet as inactive
|
||||||
|
bullets[i].position.y = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime = time(NULL);
|
||||||
|
isGameOver = 0;
|
||||||
|
finalTime = 0; // Reset final time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the spaceship on the screen
|
||||||
|
void drawSpaceship() {
|
||||||
|
int x = (int)spaceship.position.x;
|
||||||
|
int y = (int)spaceship.position.y;
|
||||||
|
int size = 3;
|
||||||
|
|
||||||
|
float cosAngle = cos(spaceship.angle);
|
||||||
|
float sinAngle = sin(spaceship.angle);
|
||||||
|
|
||||||
|
int x1 = x + size * cosAngle;
|
||||||
|
int y1 = y + size * sinAngle;
|
||||||
|
int x2 = x + size * cos(spaceship.angle + 2.5);
|
||||||
|
int y2 = y + size * sin(spaceship.angle + 2.5);
|
||||||
|
int x3 = x + size * cos(spaceship.angle - 2.5);
|
||||||
|
int y3 = y + size * sin(spaceship.angle - 2.5);
|
||||||
|
|
||||||
|
drawLine(x1, y1, x2, y2);
|
||||||
|
drawLine(x2, y2, x3, y3);
|
||||||
|
drawLine(x3, y3, x1, y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw all entities on the screen
|
||||||
|
void drawEntities(GameObject *entities, int count, void (*drawFunc)(GameObject *)) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
drawFunc(&entities[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a bullet on the screen
|
||||||
|
void drawBullet(GameObject *bullet) { // Changed to non-const
|
||||||
|
if (bullet->position.x >= 0) {
|
||||||
|
drawPixel((int)bullet->position.x, (int)bullet->position.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw an asteroid on the screen
|
||||||
|
void drawAsteroid(GameObject *asteroid) { // Changed to non-const
|
||||||
|
drawCircle((int)asteroid->position.x, (int)asteroid->position.y, (int)asteroid->radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the display
|
||||||
|
void updateDisplay() {
|
||||||
|
clearDisplay();
|
||||||
|
if (!isGameOver) {
|
||||||
|
drawSpaceship();
|
||||||
|
drawEntities(asteroids, MAX_ASTEROIDS, drawAsteroid);
|
||||||
|
drawEntities(bullets, MAX_BULLETS, drawBullet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the screen buffer
|
||||||
|
printf("\033[H");
|
||||||
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
|
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
||||||
|
putchar(display[y][x]);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display score and elapsed time
|
||||||
|
time_t currentTime = time(NULL);
|
||||||
|
int elapsedTime = isGameOver ? finalTime : (currentTime - startTime);
|
||||||
|
printf("Score: %d | Time: %02d:%02d | %s\n", score, elapsedTime / 60, elapsedTime % 60, isGameOver ? "Game Over!" : " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the position of game objects
|
||||||
|
void updateGameObject(GameObject *obj, int isBullet) {
|
||||||
|
obj->position.x += obj->velocity.x;
|
||||||
|
obj->position.y += obj->velocity.y;
|
||||||
|
|
||||||
|
// If it's a bullet, check if it's out of bounds
|
||||||
|
if (isBullet) {
|
||||||
|
if (obj->position.x < 0 || obj->position.x >= SCREEN_WIDTH || obj->position.y < 0 || obj->position.y >= SCREEN_HEIGHT) {
|
||||||
|
obj->position.x = -1; // Deactivate bullet
|
||||||
|
obj->position.y = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wrapPosition(&obj->position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the game state
|
||||||
|
void updateGame() {
|
||||||
|
if (isGameOver) return;
|
||||||
|
|
||||||
|
// Update spaceship and apply friction
|
||||||
|
updateGameObject(&spaceship, 0); // 0 indicates it's not a bullet
|
||||||
|
spaceship.velocity.x *= 0.98;
|
||||||
|
spaceship.velocity.y *= 0.98;
|
||||||
|
|
||||||
|
// Move asteroids and check for collisions
|
||||||
|
for (int i = 0; i < MAX_ASTEROIDS; i++) {
|
||||||
|
updateGameObject(&asteroids[i], 0);
|
||||||
|
if (checkCollision(&spaceship, &asteroids[i])) {
|
||||||
|
isGameOver = 1;
|
||||||
|
finalTime = time(NULL) - startTime;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update bullet positions
|
||||||
|
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||||
|
if (bullets[i].position.x >= 0) {
|
||||||
|
updateGameObject(&bullets[i], 1); // 1 indicates it's a bullet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for bullet collisions with asteroids
|
||||||
|
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||||
|
if (bullets[i].position.x >= 0) {
|
||||||
|
for (int j = 0; j < MAX_ASTEROIDS; j++) {
|
||||||
|
if (checkCollision(&bullets[i], &asteroids[j])) {
|
||||||
|
bullets[i].position.x = -1; // Deactivate bullet
|
||||||
|
bullets[i].position.y = -1;
|
||||||
|
asteroids[j].position.x = rand() % SCREEN_WIDTH;
|
||||||
|
asteroids[j].position.y = rand() % SCREEN_HEIGHT;
|
||||||
|
score += 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire a bullet
|
||||||
|
void shootBullet() {
|
||||||
|
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||||
|
if (bullets[i].position.x < 0) {
|
||||||
|
bullets[i].position = spaceship.position;
|
||||||
|
bullets[i].velocity.x = cos(spaceship.angle) * 2;
|
||||||
|
bullets[i].velocity.y = sin(spaceship.angle) * 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a key was hit
|
||||||
|
int isKeyHit() {
|
||||||
|
struct timeval tv = { 0L, 0L };
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(0, &fds);
|
||||||
|
return select(1, &fds, NULL, NULL, &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure terminal settings
|
||||||
|
void configureTerminal(struct termios *old_tio, struct termios *new_tio) {
|
||||||
|
tcgetattr(STDIN_FILENO, old_tio);
|
||||||
|
*new_tio = *old_tio;
|
||||||
|
new_tio->c_lflag &= (~ICANON & ~ECHO);
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, new_tio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore terminal settings
|
||||||
|
void restoreTerminal(struct termios *old_tio) {
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, old_tio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSignal(int sig) {
|
||||||
|
shouldExit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main game loop
|
||||||
|
int main() {
|
||||||
|
signal(SIGINT, onSignal); // Capture ^C
|
||||||
|
srand(time(NULL)); // Seed the random number generator
|
||||||
|
initGame(); // Initialize the game state
|
||||||
|
|
||||||
|
struct termios old_tio, new_tio;
|
||||||
|
configureTerminal(&old_tio, &new_tio);
|
||||||
|
|
||||||
|
printf("\033[?25l"); // Hide the cursor
|
||||||
|
|
||||||
|
while (!shouldExit) {
|
||||||
|
if (isKeyHit()) {
|
||||||
|
char input = getchar();
|
||||||
|
if (input == 27) { // ESC key
|
||||||
|
if (getchar() == '[') { // Handle arrow keys
|
||||||
|
switch (getchar()) {
|
||||||
|
case 'A': // Up arrow
|
||||||
|
spaceship.velocity.x += cos(spaceship.angle) * 0.2;
|
||||||
|
spaceship.velocity.y += sin(spaceship.angle) * 0.2;
|
||||||
|
break;
|
||||||
|
case 'B': // Down arrow
|
||||||
|
spaceship.velocity.x -= cos(spaceship.angle) * 0.2;
|
||||||
|
spaceship.velocity.y -= sin(spaceship.angle) * 0.2;
|
||||||
|
break;
|
||||||
|
case 'D': spaceship.angle -= 0.2; break; // Left arrow
|
||||||
|
case 'C': spaceship.angle += 0.2; break; // Right arrow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (input == ' ') {
|
||||||
|
shootBullet(); // Fire a bullet
|
||||||
|
} else if (input == 'q') {
|
||||||
|
break; // Quit the game
|
||||||
|
} else if (input == 'r' && isGameOver) {
|
||||||
|
initGame(); // Restart the game
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGame(); // Update game state
|
||||||
|
updateDisplay(); // Refresh the display
|
||||||
|
usleep(50000); // Wait for 50ms (20 FPS)
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\033[?25h"); // Show the cursor
|
||||||
|
restoreTerminal(&old_tio); // Restore terminal settings
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -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));
|
unassert(!pthread_attr_setstacksize(&attr, 65536 - getpagesize()));
|
||||||
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
Normal file
322
examples/rote.c
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
#/*────────────────────────────────────────────────────────────────╗
|
||||||
|
┌┘ To the extent possible under law, Justine Tunney has waived │
|
||||||
|
│ all copyright and related or neighboring rights to this file, │
|
||||||
|
│ as it is written in the following disclaimers: │
|
||||||
|
│ • http://unlicense.org/ │
|
||||||
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview cosmopolitan flash cards viewer
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Card {
|
||||||
|
char* qa[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
atomic_int g_done;
|
||||||
|
|
||||||
|
void onsig(int sig) {
|
||||||
|
g_done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* xmalloc(int n) {
|
||||||
|
void* p;
|
||||||
|
if ((p = malloc(n)))
|
||||||
|
return p;
|
||||||
|
perror("malloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* xrealloc(void* p, int n) {
|
||||||
|
if ((p = realloc(p, n)))
|
||||||
|
return p;
|
||||||
|
perror("realloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* xstrcat(const char* a, const char* b) {
|
||||||
|
char* p;
|
||||||
|
size_t n, m;
|
||||||
|
n = strlen(a);
|
||||||
|
m = strlen(b);
|
||||||
|
p = xmalloc(n + m + 1);
|
||||||
|
memcpy(p, a, n);
|
||||||
|
memcpy(p + n, b, m + 1);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shuffle(struct Card* a, int n) {
|
||||||
|
while (n > 1) {
|
||||||
|
int i = rand() % n--;
|
||||||
|
struct Card t = a[i];
|
||||||
|
a[i] = a[n];
|
||||||
|
a[n] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* trim(char* s) {
|
||||||
|
int i;
|
||||||
|
if (s) {
|
||||||
|
while (isspace(*s))
|
||||||
|
++s;
|
||||||
|
for (i = strlen(s); i--;) {
|
||||||
|
if (isspace(s[i])) {
|
||||||
|
s[i] = 0;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* readline(FILE* f) {
|
||||||
|
for (;;) {
|
||||||
|
char* line = trim(fgetln(f, 0));
|
||||||
|
if (!line)
|
||||||
|
return 0;
|
||||||
|
if (*line != '#')
|
||||||
|
if (*line)
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* fill(const char* text, int max_line_width, int* out_line_count) {
|
||||||
|
int text_len = strlen(text);
|
||||||
|
char* result = xmalloc(text_len * 2 + 1);
|
||||||
|
int result_pos = 0;
|
||||||
|
int line_start = 0;
|
||||||
|
int line_count = 1;
|
||||||
|
int i = 0;
|
||||||
|
while (i < text_len && isspace(text[i]))
|
||||||
|
i++;
|
||||||
|
while (i < text_len) {
|
||||||
|
int word_end = i;
|
||||||
|
while (word_end < text_len && !isspace(text[word_end]))
|
||||||
|
word_end++;
|
||||||
|
int word_length = word_end - i;
|
||||||
|
if ((result_pos - line_start) + (result_pos > line_start ? 1 : 0) +
|
||||||
|
word_length >
|
||||||
|
max_line_width) {
|
||||||
|
if (result_pos > line_start) {
|
||||||
|
++line_count;
|
||||||
|
result[result_pos++] = '\n';
|
||||||
|
line_start = result_pos;
|
||||||
|
}
|
||||||
|
} else if (result_pos > line_start) {
|
||||||
|
result[result_pos++] = ' ';
|
||||||
|
}
|
||||||
|
memcpy(result + result_pos, text + i, word_length);
|
||||||
|
result_pos += word_length;
|
||||||
|
i = word_end;
|
||||||
|
while (i < text_len && isspace(text[i]))
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
result[result_pos] = '\0';
|
||||||
|
result = xrealloc(result, result_pos + 1);
|
||||||
|
if (out_line_count)
|
||||||
|
*out_line_count = line_count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show(const char* text, int i, int n) {
|
||||||
|
|
||||||
|
// get pseudoteletypewriter dimensions
|
||||||
|
struct winsize ws = {80, 25};
|
||||||
|
tcgetwinsize(1, &ws);
|
||||||
|
int width = ws.ws_col;
|
||||||
|
if (width > (int)(ws.ws_col * .9))
|
||||||
|
width = ws.ws_col * .9;
|
||||||
|
if (width > 80)
|
||||||
|
width = 80;
|
||||||
|
width &= -2;
|
||||||
|
|
||||||
|
// clear display
|
||||||
|
printf("\033[H\033[J");
|
||||||
|
|
||||||
|
// display flash card text in middle of display
|
||||||
|
char buf[32];
|
||||||
|
int line_count;
|
||||||
|
char* lines = fill(text, width, &line_count);
|
||||||
|
sprintf(buf, "%d/%d\r\n\r\n", i + 1, n);
|
||||||
|
line_count += 2;
|
||||||
|
char* extra = xstrcat(buf, lines);
|
||||||
|
free(lines);
|
||||||
|
char* tokens = extra;
|
||||||
|
for (int j = 0;; ++j) {
|
||||||
|
char* line = strtok(tokens, "\n");
|
||||||
|
tokens = 0;
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
printf("\033[%d;%dH%s", ws.ws_row / 2 - line_count / 2 + j + 1,
|
||||||
|
ws.ws_col / 2 - strlen(line) / 2 + 1, line);
|
||||||
|
}
|
||||||
|
free(extra);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(FILE* f, const char* prog) {
|
||||||
|
fprintf(f,
|
||||||
|
"usage: %s FILE\n"
|
||||||
|
"\n"
|
||||||
|
"here's an example of what your file should look like:\n"
|
||||||
|
"\n"
|
||||||
|
" # cosmopolitan flash cards\n"
|
||||||
|
" # california dmv drivers test\n"
|
||||||
|
" \n"
|
||||||
|
" which of the following point totals could result in "
|
||||||
|
"your license being suspended by the dmv?\n"
|
||||||
|
" 4 points in 12 months (middle)\n"
|
||||||
|
" \n"
|
||||||
|
" at 55 mph under good conditions a passenger vehicle can stop "
|
||||||
|
"within\n"
|
||||||
|
" 300 feet (not 200, not 400, middle)\n"
|
||||||
|
" \n"
|
||||||
|
" two sets of solid double yellow lines spaced two or more feet "
|
||||||
|
"apart indicate\n"
|
||||||
|
" a BARRIER (do not cross unless there's an opening)\n"
|
||||||
|
"\n"
|
||||||
|
"more specifically, empty lines are ignored, lines starting with\n"
|
||||||
|
"a hash are ignored, then an even number of lines must remain,\n"
|
||||||
|
"where each two lines is a card, holding question and answer.\n",
|
||||||
|
prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
|
// show help
|
||||||
|
if (argc != 2) {
|
||||||
|
usage(stderr, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[1], "-?") || //
|
||||||
|
!strcmp(argv[1], "-h") || //
|
||||||
|
!strcmp(argv[1], "--help")) {
|
||||||
|
usage(stdout, argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// teletypewriter is required
|
||||||
|
if (!isatty(0) || !isatty(1)) {
|
||||||
|
perror("isatty");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load cards
|
||||||
|
FILE* f = fopen(argv[1], "r");
|
||||||
|
if (!f) {
|
||||||
|
perror(argv[1]);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
struct Card* cards = 0;
|
||||||
|
for (;;) {
|
||||||
|
struct Card card;
|
||||||
|
if (!(card.qa[0] = readline(f)))
|
||||||
|
break;
|
||||||
|
card.qa[0] = strdup(card.qa[0]);
|
||||||
|
if (!(card.qa[1] = readline(f))) {
|
||||||
|
fprintf(stderr, "%s: flash card file has odd number of lines\n", argv[1]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
card.qa[1] = strdup(card.qa[1]);
|
||||||
|
cards = xrealloc(cards, (count + 1) * sizeof(struct Card));
|
||||||
|
cards[count++] = card;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
// randomize
|
||||||
|
srand(time(0));
|
||||||
|
shuffle(cards, count);
|
||||||
|
|
||||||
|
// catch ctrl-c
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
sa.sa_handler = onsig;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sigaction(SIGINT, &sa, 0);
|
||||||
|
|
||||||
|
// enter raw mode
|
||||||
|
struct termios ot;
|
||||||
|
tcgetattr(1, &ot);
|
||||||
|
struct termios nt = ot;
|
||||||
|
cfmakeraw(&nt);
|
||||||
|
nt.c_lflag |= ISIG;
|
||||||
|
tcsetattr(1, TCSANOW, &nt);
|
||||||
|
printf("\033[?25l");
|
||||||
|
|
||||||
|
// show flash cards
|
||||||
|
int i = 0;
|
||||||
|
while (!g_done) {
|
||||||
|
show(cards[i / 2].qa[i % 2], i / 2, count);
|
||||||
|
|
||||||
|
// press any key
|
||||||
|
char b[8] = {0};
|
||||||
|
read(0, b, sizeof(b));
|
||||||
|
|
||||||
|
// q quits
|
||||||
|
if (b[0] == 'q')
|
||||||
|
break;
|
||||||
|
|
||||||
|
// b or ctrl-b goes backward
|
||||||
|
if (b[0] == 'b' || //
|
||||||
|
b[0] == ('B' ^ 0100)) {
|
||||||
|
if (--i < 0)
|
||||||
|
i = count * 2 - 1;
|
||||||
|
i &= -2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// p or ctrl-p goes backward
|
||||||
|
if (b[0] == 'p' || //
|
||||||
|
b[0] == ('P' ^ 0100)) {
|
||||||
|
if (--i < 0)
|
||||||
|
i = count * 2 - 1;
|
||||||
|
i &= -2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// up arrow goes backward
|
||||||
|
if (b[0] == 033 && //
|
||||||
|
b[1] == '[' && //
|
||||||
|
b[2] == 'A') {
|
||||||
|
if (--i < 0)
|
||||||
|
i = count * 2 - 1;
|
||||||
|
i &= -2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// left arrow goes backward
|
||||||
|
if (b[0] == 033 && //
|
||||||
|
b[1] == '[' && //
|
||||||
|
b[2] == 'D') {
|
||||||
|
if (--i < 0)
|
||||||
|
i = count * 2 - 1;
|
||||||
|
i &= -2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only advance
|
||||||
|
if (++i == count * 2)
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free memory
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
for (int j = 0; j < 2; ++j)
|
||||||
|
free(cards[i].qa[j]);
|
||||||
|
free(cards);
|
||||||
|
|
||||||
|
// cleanup terminal and show cursor
|
||||||
|
tcsetattr(1, TCSANOW, &ot);
|
||||||
|
printf("\033[?25h");
|
||||||
|
printf("\n");
|
||||||
|
}
|
|
@ -7,9 +7,13 @@
|
||||||
│ • 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"
|
||||||
|
|
||||||
|
@ -67,8 +71,18 @@ 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]));
|
||||||
}
|
}
|
||||||
qsort(things.p, things.n, sizeof(*things.p), Compare);
|
if (!IsWindows()) {
|
||||||
for (int i = 0; i < things.n; ++i) {
|
struct AddrSize stak = __get_main_stack();
|
||||||
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
|
Append((intptr_t)stak.addr + stak.size, "top of stack");
|
||||||
|
Append((intptr_t)stak.addr, "bottom of stack");
|
||||||
|
} else {
|
||||||
|
#ifdef __x86_64__
|
||||||
|
Append(GetStaticStackAddr(0) + GetStaticStackSize(), "top of stack");
|
||||||
|
Append(GetStaticStackAddr(0) + GetGuardSize(), "bottom of stack");
|
||||||
|
Append(GetStaticStackAddr(0), "bottom of guard region");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
qsort(things.p, things.n, sizeof(*things.p), Compare);
|
||||||
|
for (int i = 0; i < things.n; ++i)
|
||||||
|
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
|
||||||
}
|
}
|
||||||
|
|
17
examples/thread.c
Normal file
17
examples/thread.c
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// how to spawn a thread
|
||||||
|
|
||||||
|
void *my_thread(void *arg) {
|
||||||
|
printf("my_thread(%p) is running\n", arg);
|
||||||
|
return (void *)0x456L;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
void *res;
|
||||||
|
pthread_t th;
|
||||||
|
pthread_create(&th, 0, my_thread, (void *)0x123L);
|
||||||
|
pthread_join(th, &res);
|
||||||
|
printf("my_thread() returned %p\n", res);
|
||||||
|
}
|
|
@ -216,12 +216,6 @@ 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 $<
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ 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"
|
||||||
|
@ -23,26 +24,37 @@
|
||||||
#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) {
|
||||||
uint32_t msdelay;
|
struct timespec now, wall;
|
||||||
struct timespec now;
|
uint32_t minRes, maxRes, oldRes;
|
||||||
for (;;) {
|
sys_clock_gettime_nt(0, &wall);
|
||||||
if (sys_clock_gettime_nt(clock, &now))
|
if (sys_clock_gettime_nt(clock, &now))
|
||||||
return -1;
|
return -1;
|
||||||
if (timespec_cmp(now, abs) >= 0)
|
bool wantRes = clock == CLOCK_REALTIME || //
|
||||||
return 0;
|
clock == CLOCK_MONOTONIC || //
|
||||||
msdelay = timespec_tomillis(timespec_sub(abs, now));
|
clock == CLOCK_BOOTTIME;
|
||||||
msdelay = MIN(msdelay, -1u);
|
if (wantRes && !atomic_fetch_add(&usingRes, 1))
|
||||||
if (_park_norestart(msdelay, waitmask) == -1)
|
changedRes = NtSuccess(NtQueryTimerResolution(&minRes, &maxRes, &oldRes)) &&
|
||||||
return -1;
|
NtSuccess(NtSetTimerResolution(maxRes, true, &oldRes));
|
||||||
}
|
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,9 +23,9 @@
|
||||||
#include "libc/sysv/consts/clock.h"
|
#include "libc/sysv/consts/clock.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
int sys_clock_nanosleep_openbsd(int clock, int flags,
|
relegated 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;
|
||||||
struct timespec start, relative, remainder;
|
struct timespec start, relative, remainder;
|
||||||
if (!flags) {
|
if (!flags) {
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
*
|
*
|
||||||
* @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,6 +51,7 @@
|
||||||
#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 {
|
||||||
|
@ -67,7 +68,9 @@ 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;
|
||||||
|
@ -110,7 +113,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;
|
||||||
|
@ -122,7 +125,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) {
|
||||||
|
@ -353,9 +356,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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,24 +21,23 @@
|
||||||
#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;
|
||||||
BLOCK_SIGNALS;
|
__cxa_lock();
|
||||||
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) {
|
||||||
|
@ -54,12 +53,11 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
|
||||||
} else {
|
} else {
|
||||||
rc = __winerr();
|
rc = __winerr();
|
||||||
}
|
}
|
||||||
pthread_spin_unlock(&lock);
|
__cxa_unlock();
|
||||||
ALLOW_SIGNALS;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__constructor__(40))) static textstartup void ntinitload(void) {
|
CTOR static textstartup void sys_getloadavg_nt_init(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:
|
||||||
push %rbp
|
beg
|
||||||
mov %rsp,%rbp
|
pro
|
||||||
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,6 +55,7 @@ __getntsyspath:
|
||||||
jne 2f
|
jne 2f
|
||||||
movb $'/',-1(%rdi)
|
movb $'/',-1(%rdi)
|
||||||
2: .loop 1b
|
2: .loop 1b
|
||||||
leave
|
epi
|
||||||
ret
|
ret
|
||||||
|
end
|
||||||
.endfn __getntsyspath,globl,hidden
|
.endfn __getntsyspath,globl,hidden
|
||||||
|
|
|
@ -96,9 +96,8 @@ 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;
|
||||||
|
@ -118,9 +117,10 @@ 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 (!CopyWithCwd(q, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf))) {
|
if (!q)
|
||||||
|
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,9 +129,8 @@ 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) {
|
||||||
|
@ -212,14 +211,12 @@ 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,7 +114,8 @@ 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()) {
|
||||||
rc = ProcessPrng(p, n) ? n : __winerr();
|
ProcessPrng(p, n); // never fails
|
||||||
|
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);
|
||||||
|
@ -184,9 +185,7 @@ 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,6 +21,7 @@
|
||||||
#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"
|
||||||
|
@ -47,8 +48,7 @@ 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->rlim_cur = GetStaticStackSize();
|
*rlim = __rlimit_stack_get();
|
||||||
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,6 +3,7 @@
|
||||||
#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"
|
||||||
|
@ -30,8 +31,10 @@ 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;
|
||||||
|
@ -45,8 +48,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(uint32_t, uint64_t);
|
int _park_norestart(struct timespec, uint64_t);
|
||||||
int _park_restartable(uint32_t, uint64_t);
|
int _park_restartable(struct timespec, 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,10 +67,9 @@ 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;
|
||||||
struct DirectMap dm;
|
void *addr = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE,
|
||||||
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,6 +17,7 @@
|
||||||
│ 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"
|
||||||
|
@ -38,12 +39,15 @@
|
||||||
#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];
|
||||||
|
@ -63,10 +67,12 @@ 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 = (ReadFile(fh, buf, len, 0, &overlap) ||
|
ok = overlap.hEvent &&
|
||||||
|
(ReadFile(fh, buf, len, 0, &overlap) ||
|
||||||
GetLastError() == kNtErrorIoPending) &&
|
GetLastError() == kNtErrorIoPending) &&
|
||||||
GetOverlappedResult(fh, &overlap, &got, true);
|
GetOverlappedResult(fh, &overlap, &got, true);
|
||||||
CloseHandle(overlap.hEvent);
|
if (overlap.hEvent)
|
||||||
|
__imp_CloseHandle(overlap.hEvent);
|
||||||
return ok ? got : -1;
|
return ok ? got : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +92,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);
|
||||||
CloseHandle(fh);
|
__imp_CloseHandle(fh);
|
||||||
if (got < 3)
|
if (got < 3)
|
||||||
return enoexec();
|
return enoexec();
|
||||||
pe = p + got;
|
pe = p + got;
|
||||||
|
|
|
@ -49,11 +49,9 @@ 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)) {
|
||||||
struct DirectMap dm;
|
state = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096),
|
||||||
dm = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096),
|
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
|
||||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1,
|
-1, 0);
|
||||||
0);
|
|
||||||
state = dm.addr;
|
|
||||||
if (state == (void *)-1)
|
if (state == (void *)-1)
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,65 +19,96 @@
|
||||||
#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 on timeout or spurious wakeup
|
// returns 0 if deadline is reached
|
||||||
// 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(uint32_t msdelay, sigset_t waitmask,
|
textwindows static int _park_thread(struct timespec deadline, sigset_t waitmask,
|
||||||
bool restartable) {
|
bool restartable) {
|
||||||
struct PosixThread *pt = _pthread_self();
|
for (;;) {
|
||||||
|
uint32_t handl = 0;
|
||||||
|
intptr_t hands[2];
|
||||||
|
|
||||||
// perform the wait operation
|
// create event object
|
||||||
intptr_t sigev;
|
intptr_t sigev;
|
||||||
if (!(sigev = CreateEvent(0, 0, 0, 0)))
|
if (!(sigev = CreateEvent(0, 0, 0, 0)))
|
||||||
return __winerr();
|
return __winerr();
|
||||||
pt->pt_event = sigev;
|
hands[handl++] = sigev;
|
||||||
pt->pt_blkmask = waitmask;
|
|
||||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT,
|
|
||||||
memory_order_release);
|
|
||||||
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
|
|
||||||
int sig = 0;
|
|
||||||
uint32_t ws = 0;
|
|
||||||
if (!_is_canceled() &&
|
|
||||||
!(_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))))
|
|
||||||
ws = WaitForSingleObject(sigev, msdelay);
|
|
||||||
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
|
|
||||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
|
||||||
CloseHandle(sigev);
|
|
||||||
|
|
||||||
// recursion is now safe
|
// create high precision timer if needed
|
||||||
if (ws == -1u)
|
if (memcmp(&deadline, ×pec_max, sizeof(struct timespec))) {
|
||||||
return __winerr();
|
intptr_t hTimer;
|
||||||
int handler_was_called = 0;
|
if ((hTimer = CreateWaitableTimer(NULL, true, NULL))) {
|
||||||
if (sig)
|
int64_t due = TimeSpecToWindowsTime(deadline);
|
||||||
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
if (SetWaitableTimer(hTimer, &due, 0, NULL, NULL, false)) {
|
||||||
if (_check_cancel())
|
hands[handl++] = hTimer;
|
||||||
return -1;
|
} else {
|
||||||
if (handler_was_called & SIG_HANDLED_NO_RESTART)
|
CloseHandle(hTimer);
|
||||||
return eintr();
|
}
|
||||||
if (handler_was_called & SIG_HANDLED_SA_RESTART)
|
}
|
||||||
if (!restartable)
|
}
|
||||||
|
|
||||||
|
// perform wait operation
|
||||||
|
struct PosixThread *pt = _pthread_self();
|
||||||
|
pt->pt_event = sigev;
|
||||||
|
pt->pt_blkmask = waitmask;
|
||||||
|
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT,
|
||||||
|
memory_order_release);
|
||||||
|
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
|
||||||
|
int sig = 0;
|
||||||
|
uint32_t wi = 0;
|
||||||
|
if (!_is_canceled() &&
|
||||||
|
!(_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))))
|
||||||
|
wi = WaitForMultipleObjects(handl, hands, false, -1u);
|
||||||
|
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
|
||||||
|
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||||
|
for (int i = 0; i < handl; ++i)
|
||||||
|
CloseHandle(hands[i]);
|
||||||
|
|
||||||
|
// recursion is now safe
|
||||||
|
if (wi == 1)
|
||||||
|
return 0;
|
||||||
|
if (wi == -1u)
|
||||||
|
return __winerr();
|
||||||
|
int handler_was_called = 0;
|
||||||
|
if (!sig) {
|
||||||
|
if (_check_cancel())
|
||||||
|
return -1;
|
||||||
|
if (_weaken(__sig_get))
|
||||||
|
sig = _weaken(__sig_get)(waitmask);
|
||||||
|
}
|
||||||
|
if (sig)
|
||||||
|
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
||||||
|
if (_check_cancel())
|
||||||
|
return -1;
|
||||||
|
if (handler_was_called & SIG_HANDLED_NO_RESTART)
|
||||||
return eintr();
|
return eintr();
|
||||||
return 0;
|
if (handler_was_called & SIG_HANDLED_SA_RESTART)
|
||||||
|
if (!restartable)
|
||||||
|
return eintr();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textwindows int _park_norestart(uint32_t msdelay, sigset_t waitmask) {
|
textwindows int _park_norestart(struct timespec deadline, sigset_t waitmask) {
|
||||||
return _park_thread(msdelay, waitmask, false);
|
return _park_thread(deadline, waitmask, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
textwindows int _park_restartable(uint32_t msdelay, sigset_t waitmask) {
|
textwindows int _park_restartable(struct timespec deadline, sigset_t waitmask) {
|
||||||
return _park_thread(msdelay, waitmask, true);
|
return _park_thread(deadline, waitmask, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -18,21 +18,20 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#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;
|
||||||
while (!(rc = _park_norestart(-1u, 0)))
|
_park_norestart(timespec_max, 0);
|
||||||
donothing;
|
|
||||||
ALLOW_SIGNALS;
|
ALLOW_SIGNALS;
|
||||||
return rc;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -694,6 +694,7 @@ 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,9 +344,16 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
}
|
}
|
||||||
if (got)
|
if (got)
|
||||||
return got;
|
return got;
|
||||||
if (!(waitms = sys_poll_nt_waitms(deadline)))
|
now = sys_clock_gettime_monotonic_nt();
|
||||||
|
if (timespec_cmp(now, deadline) >= 0)
|
||||||
return 0;
|
return 0;
|
||||||
if (_park_norestart(waitms, waitmask) == -1)
|
next = timespec_add(now, timespec_frommillis(POLL_INTERVAL_MS));
|
||||||
|
if (timespec_cmp(next, deadline) >= 0) {
|
||||||
|
target = deadline;
|
||||||
|
} else {
|
||||||
|
target = next;
|
||||||
|
}
|
||||||
|
if (_park_norestart(target, waitmask) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#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"
|
||||||
|
@ -127,33 +128,46 @@ struct Keystrokes {
|
||||||
bool ohno_decckm;
|
bool ohno_decckm;
|
||||||
bool bypass_mode;
|
bool bypass_mode;
|
||||||
uint16_t utf16hs;
|
uint16_t utf16hs;
|
||||||
int16_t freekeys;
|
size_t free_keys;
|
||||||
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 WipeKeystrokes(void) {
|
textwindows void sys_read_nt_wipe_keystrokes(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.freekeys;
|
++__keystroke.free_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = dll_first(__keystroke.free);
|
struct Dll *e;
|
||||||
if (!e) // See MIN(freekeys) before ReadConsoleInput()
|
struct Keystroke *k;
|
||||||
__builtin_trap();
|
if ((e = dll_first(__keystroke.free))) {
|
||||||
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
dll_remove(&__keystroke.free, e);
|
||||||
dll_remove(&__keystroke.free, &k->elem);
|
k = KEYSTROKE_CONTAINER(e);
|
||||||
--__keystroke.freekeys;
|
--__keystroke.free_keys;
|
||||||
|
} else {
|
||||||
|
// PopulateKeystrokes() should make this branch impossible
|
||||||
|
if (!(k = AllocKeystroke()))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
k->buflen = 0;
|
k->buflen = 0;
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
@ -169,15 +183,22 @@ 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) {
|
||||||
|
@ -191,11 +212,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) {
|
||||||
|
@ -320,9 +341,12 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,7 +481,8 @@ textwindows static void WriteCtl(const char *p, size_t n, bool escape_harder) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textwindows static void EchoTty(const char *p, size_t n, bool escape_harder) {
|
textwindows void EchoConsoleNt(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);
|
||||||
|
@ -514,14 +539,12 @@ 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);
|
||||||
EchoTty(buf, len, true);
|
EchoConsoleNt(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] && //
|
||||||
|
@ -611,12 +634,14 @@ 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
|
||||||
EchoTty(buf, len, false);
|
EchoConsoleNt(buf, len, false);
|
||||||
|
|
||||||
// save keystroke to appropriate list
|
// save keystroke to appropriate list
|
||||||
if (__ttyconf.magic & kTtyUncanon) {
|
if (__ttyconf.magic & kTtyUncanon) {
|
||||||
|
@ -624,12 +649,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 if oom or enter
|
// flush canonical mode line on enter
|
||||||
if (!__keystroke.freekeys || (len == 1 && buf[0] &&
|
if (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;
|
||||||
}
|
}
|
||||||
|
@ -640,15 +665,17 @@ 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 || !__keystroke.freekeys)
|
if (n > ARRAYLEN(records))
|
||||||
|
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)
|
||||||
|
@ -970,8 +997,10 @@ 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) {
|
||||||
return ProcessPrng(data, size) ? size : __winerr();
|
ProcessPrng(data, size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
if (f->kind == kFdConsole)
|
if (f->kind == kFdConsole)
|
||||||
return ReadFromConsole(f, data, size, waitmask);
|
return ReadFromConsole(f, data, size, waitmask);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#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"
|
||||||
|
@ -88,10 +89,12 @@ 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 = enotsup();
|
rc = 0;
|
||||||
} 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,9 +35,8 @@ 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
717
libc/calls/sig.c
|
@ -1,717 +0,0 @@
|
||||||
/*-*- 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_handler = OnSigSegv,
|
* struct sigaction sa = {.sa_sigaction = OnCrash,
|
||||||
* .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_MINSIGSTKSZ) + 8192;
|
* ss.ss_size = sysconf(_SC_SIGSTKSZ);
|
||||||
* 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,11 +121,16 @@ 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 less than `MINSIGSTKSZ`
|
* @raise ENOMEM if `neu->ss_size` is beneath `sysconf(_SC_MINSIGSTKSZ)`
|
||||||
*/
|
*/
|
||||||
int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
|
int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#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.
|
||||||
|
@ -513,6 +514,7 @@ 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) {
|
||||||
|
@ -521,6 +523,13 @@ 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,6 +21,7 @@
|
||||||
#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"
|
||||||
|
@ -59,8 +60,7 @@ 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;
|
||||||
while (!(rc = _park_norestart(-1u, waitmask)))
|
rc = _park_norestart(timespec_max, 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,7 +13,6 @@ 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,19 +4,21 @@ COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
typedef uint64_t sigset_t;
|
typedef uint64_t sigset_t;
|
||||||
|
|
||||||
int sigaddset(sigset_t *, int) paramsnonnull();
|
/* clang-format off */
|
||||||
int sigdelset(sigset_t *, int) paramsnonnull();
|
int sigaddset(sigset_t *, int) libcesque paramsnonnull();
|
||||||
int sigemptyset(sigset_t *) paramsnonnull();
|
int sigdelset(sigset_t *, int) libcesque paramsnonnull();
|
||||||
int sigfillset(sigset_t *) paramsnonnull();
|
int sigemptyset(sigset_t *) libcesque paramsnonnull();
|
||||||
int sigandset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
|
int sigfillset(sigset_t *) libcesque paramsnonnull();
|
||||||
int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
|
int sigandset(sigset_t *, const sigset_t *, const sigset_t *) libcesque paramsnonnull();
|
||||||
int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect;
|
int sigorset(sigset_t *, const sigset_t *, const sigset_t *) libcesque paramsnonnull();
|
||||||
int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
|
int sigisemptyset(const sigset_t *) libcesque paramsnonnull() nosideeffect;
|
||||||
int sigcountset(const sigset_t *) paramsnonnull() nosideeffect;
|
int sigismember(const sigset_t *, int) libcesque paramsnonnull() nosideeffect;
|
||||||
int sigprocmask(int, const sigset_t *, sigset_t *);
|
int sigcountset(const sigset_t *) libcesque paramsnonnull() nosideeffect;
|
||||||
int sigsuspend(const sigset_t *);
|
int sigprocmask(int, const sigset_t *, sigset_t *) dontthrow;
|
||||||
int sigpending(sigset_t *);
|
int sigsuspend(const sigset_t *) dontthrow;
|
||||||
int pthread_sigmask(int, const sigset_t *, sigset_t *);
|
int sigpending(sigset_t *) libcesque;
|
||||||
|
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,27 +5,15 @@
|
||||||
#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,13 +1,14 @@
|
||||||
#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
|
||||||
|
@ -18,6 +19,8 @@ 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]
|
||||||
|
@ -28,8 +31,5 @@ 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_ */
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
#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_ */
|
|
|
@ -2,6 +2,9 @@
|
||||||
#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);
|
||||||
|
@ -37,6 +40,7 @@ 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_ */
|
||||||
|
|
|
@ -16,10 +16,9 @@
|
||||||
│ 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/time.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/struct/timeval.h"
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/sysv/consts/clock.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns time as seconds from UNIX epoch.
|
* Returns time as seconds from UNIX epoch.
|
||||||
|
@ -29,15 +28,11 @@
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
int64_t time(int64_t *opt_out_ret) {
|
int64_t time(int64_t *opt_out_ret) {
|
||||||
int64_t secs;
|
int64_t secs = -1;
|
||||||
struct timeval tv;
|
struct timespec ts;
|
||||||
if (gettimeofday(&tv, 0) != -1) {
|
if (!clock_gettime(CLOCK_REALTIME, &ts))
|
||||||
secs = tv.tv_sec;
|
secs = ts.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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,8 @@ 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 = (ReadFile(handle, buf, 2, 0, &overlap) ||
|
ok = overlap.hEvent &&
|
||||||
|
(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,5 +1,6 @@
|
||||||
#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
|
||||||
|
@ -8,14 +9,33 @@ COSMOPOLITAN_C_START_
|
||||||
#define _COSMO_ATOMIC(x) x
|
#define _COSMO_ATOMIC(x) x
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
errno_t cosmo_once(_COSMO_ATOMIC(unsigned) *, void (*)(void)) libcesque;
|
errno_t cosmo_once(_COSMO_ATOMIC(unsigned) *, void (*)(void));
|
||||||
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,7 +47,14 @@ __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:
|
||||||
#ifdef __x86_64__
|
.cfi_startproc
|
||||||
|
#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
|
||||||
|
@ -159,4 +166,5 @@ _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,6 +57,7 @@
|
||||||
#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"
|
||||||
|
|
||||||
|
@ -131,6 +132,8 @@ 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];
|
||||||
|
|
||||||
|
@ -251,7 +254,7 @@ static bool elf_slurp(struct Loaded *l, int fd, const char *file) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dontinline bool elf_load(struct Loaded *l, const char *file, long pagesz,
|
dontinline static 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)
|
||||||
|
@ -277,7 +280,7 @@ static long *push_strs(long *sp, char **list, int count) {
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static wontreturn dontinstrument void foreign_helper(void **p) {
|
wontreturn dontinstrument static 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];
|
||||||
|
@ -285,7 +288,7 @@ static wontreturn dontinstrument void foreign_helper(void **p) {
|
||||||
_longjmp(__foreign.jb, 1);
|
_longjmp(__foreign.jb, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static dontinline void elf_exec(const char *file, char **envp) {
|
dontinline static void elf_exec(const char *file, char **envp) {
|
||||||
|
|
||||||
// get microprocessor page size
|
// get microprocessor page size
|
||||||
long pagesz = __pagesize;
|
long pagesz = __pagesize;
|
||||||
|
@ -409,7 +412,7 @@ static char *dlerror_set(const char *str) {
|
||||||
return dlerror_buf;
|
return dlerror_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dontinline char *foreign_alloc_block(void) {
|
dontinline static char *foreign_alloc_block(void) {
|
||||||
char *p = 0;
|
char *p = 0;
|
||||||
size_t sz = 65536;
|
size_t sz = 65536;
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
|
@ -432,17 +435,16 @@ static dontinline char *foreign_alloc_block(void) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dontinline void *foreign_alloc(size_t n) {
|
dontinline static void *foreign_alloc(size_t n) {
|
||||||
void *res;
|
void *res;
|
||||||
static char *block;
|
static char *block;
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
__dlopen_lock();
|
||||||
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);
|
||||||
pthread_mutex_unlock(&lock);
|
__dlopen_unlock();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +548,7 @@ static void *foreign_thunk_nt(void *func) {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dontinline bool foreign_compile(char exe[hasatleast PATH_MAX]) {
|
dontinline static 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);
|
||||||
|
@ -808,7 +810,7 @@ void *cosmo_dlopen(const char *path, int mode) {
|
||||||
}
|
}
|
||||||
ALLOW_CANCELATION;
|
ALLOW_CANCELATION;
|
||||||
ALLOW_SIGNALS;
|
ALLOW_SIGNALS;
|
||||||
STRACE("dlopen(%#s, %d) → %p% m", path, mode, res);
|
STRACE("cosmo_dlopen(%#s, %d) → %p% m", path, mode, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -853,7 +855,7 @@ void *cosmo_dlsym(void *handle, const char *name) {
|
||||||
} else {
|
} else {
|
||||||
func = 0;
|
func = 0;
|
||||||
}
|
}
|
||||||
STRACE("dlsym(%p, %#s) → %p", handle, name, func);
|
STRACE("cosmo_dlsym(%p, %#s) → %p", handle, name, func);
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,7 +890,7 @@ int cosmo_dlclose(void *handle) {
|
||||||
} else {
|
} else {
|
||||||
res = -1;
|
res = -1;
|
||||||
}
|
}
|
||||||
STRACE("dlclose(%p) → %d", handle, res);
|
STRACE("cosmo_dlclose(%p) → %d", handle, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,6 +909,6 @@ char *cosmo_dlerror(void) {
|
||||||
} else {
|
} else {
|
||||||
res = dlerror_buf;
|
res = dlerror_buf;
|
||||||
}
|
}
|
||||||
STRACE("dlerror() → %#s", res);
|
STRACE("cosmo_dlerror() → %#s", res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
│ 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.
|
||||||
|
@ -27,12 +31,13 @@
|
||||||
*
|
*
|
||||||
* @return null always
|
* @return null always
|
||||||
*/
|
*/
|
||||||
void *dlopen(const char *, int) {
|
void *dlopen(const char *path, int mode) {
|
||||||
|
STRACE("dlopen(%#s, %d) → 0 [%s]", path, mode, DLOPEN_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *dlerror(void) {
|
char *dlerror(void) {
|
||||||
return "dlopen() isn't supported by cosmo; try using cosmo_dlopen()";
|
return DLOPEN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *dlsym(void *, const char *) {
|
void *dlsym(void *, const char *) {
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
#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
|
||||||
|
|
10
libc/errno.h
10
libc/errno.h
|
@ -26,11 +26,11 @@ COSMOPOLITAN_C_START_
|
||||||
/* this header is included by 700+ files; therefore we */
|
/* this header is included by 700+ files; therefore we */
|
||||||
/* hand-roll &__get_tls()->tib_errno to avoid #include */
|
/* hand-roll &__get_tls()->tib_errno to avoid #include */
|
||||||
/* cosmopolitan uses x28 as the tls register b/c apple */
|
/* cosmopolitan uses x28 as the tls register b/c apple */
|
||||||
#define errno \
|
#define errno \
|
||||||
(*__extension__({ \
|
(*__extension__({ \
|
||||||
errno_t *__ep; \
|
errno_t *__ep; \
|
||||||
__asm__("sub\t%0,x28,#512-0x3c" : "=r"(__ep)); \
|
__asm__("sub\t%0,x28,#1024-0x3c" : "=r"(__ep)); \
|
||||||
__ep; \
|
__ep; \
|
||||||
}))
|
}))
|
||||||
#else
|
#else
|
||||||
#define errno (*__errno_location())
|
#define errno (*__errno_location())
|
||||||
|
|
|
@ -49,6 +49,6 @@
|
||||||
int __vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *,
|
int __vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *,
|
||||||
va_list);
|
va_list);
|
||||||
int __fmt(void *, void *, const char *, va_list, int *);
|
int __fmt(void *, void *, const char *, va_list, int *);
|
||||||
__msabi char16_t *__itoa16(char16_t[21], uint64_t);
|
char16_t *__itoa16(char16_t[21], uint64_t) __msabi;
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */
|
#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */
|
||||||
|
|
|
@ -135,7 +135,7 @@ typedef struct {
|
||||||
#define strftimeesque(n) __attribute__((__format__(__strftime__, n, 0)))
|
#define strftimeesque(n) __attribute__((__format__(__strftime__, n, 0)))
|
||||||
|
|
||||||
#ifndef privileged
|
#ifndef privileged
|
||||||
#define privileged _Section(".privileged") dontinline dontinstrument dontubsan
|
#define privileged _Section(".privileged") dontinstrument dontubsan
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef wontreturn
|
#ifndef wontreturn
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
#undef __COSMOPOLITAN__
|
#undef __COSMOPOLITAN__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define __COSMOPOLITAN_MAJOR__ 3
|
#define __COSMOPOLITAN_MAJOR__ 4
|
||||||
#define __COSMOPOLITAN_MINOR__ 9
|
#define __COSMOPOLITAN_MINOR__ 0
|
||||||
#define __COSMOPOLITAN_PATCH__ 5
|
#define __COSMOPOLITAN_PATCH__ 2
|
||||||
#define __COSMOPOLITAN__ \
|
#define __COSMOPOLITAN__ \
|
||||||
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
|
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
|
||||||
__COSMOPOLITAN_PATCH__)
|
__COSMOPOLITAN_PATCH__)
|
||||||
|
|
|
@ -30,9 +30,11 @@ LIBC_INTRIN_A_CHECKS = \
|
||||||
LIBC_INTRIN_A_DIRECTDEPS = \
|
LIBC_INTRIN_A_DIRECTDEPS = \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
LIBC_NT_KERNEL32 \
|
LIBC_NT_KERNEL32 \
|
||||||
|
LIBC_NT_REALTIME \
|
||||||
|
LIBC_NT_SYNCHRONIZATION \
|
||||||
LIBC_NT_WS2_32 \
|
LIBC_NT_WS2_32 \
|
||||||
LIBC_SYSV \
|
LIBC_SYSV \
|
||||||
LIBC_SYSV_CALLS
|
LIBC_SYSV_CALLS \
|
||||||
|
|
||||||
LIBC_INTRIN_A_DEPS := \
|
LIBC_INTRIN_A_DEPS := \
|
||||||
$(call uniq,$(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x))))
|
$(call uniq,$(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x))))
|
||||||
|
@ -106,14 +108,30 @@ o//libc/intrin/demangle.o: private \
|
||||||
CFLAGS += \
|
CFLAGS += \
|
||||||
-mgeneral-regs-only
|
-mgeneral-regs-only
|
||||||
|
|
||||||
|
# ensure that division is optimized
|
||||||
|
o/$(MODE)/libc/intrin/windowsdurationtotimeval.o \
|
||||||
|
o/$(MODE)/libc/intrin/windowsdurationtotimespec.o \
|
||||||
|
o/$(MODE)/libc/intrin/timevaltowindowstime.o \
|
||||||
|
o/$(MODE)/libc/intrin/timespectowindowstime.o \
|
||||||
|
o/$(MODE)/libc/intrin/windowstimetotimeval.o \
|
||||||
|
o/$(MODE)/libc/intrin/windowstimetotimespec.o: private \
|
||||||
|
CFLAGS += \
|
||||||
|
-O2
|
||||||
|
|
||||||
# these assembly files are safe to build on aarch64
|
# these assembly files are safe to build on aarch64
|
||||||
|
o/$(MODE)/libc/intrin/getcontext.o: libc/intrin/getcontext.S
|
||||||
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
|
o/$(MODE)/libc/intrin/swapcontext.o: libc/intrin/swapcontext.S
|
||||||
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
|
o/$(MODE)/libc/intrin/tailcontext.o: libc/intrin/tailcontext.S
|
||||||
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
o/$(MODE)/libc/intrin/aarch64/%.o: libc/intrin/aarch64/%.S
|
o/$(MODE)/libc/intrin/aarch64/%.o: libc/intrin/aarch64/%.S
|
||||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
o/$(MODE)/libc/intrin/fenv.o: libc/intrin/fenv.S
|
o/$(MODE)/libc/intrin/fenv.o: libc/intrin/fenv.S
|
||||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
o/$(MODE)/libc/intrin/gcov.o: libc/intrin/gcov.S
|
o/$(MODE)/libc/intrin/gcov.o: libc/intrin/gcov.S
|
||||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
o/$(MODE)/libc/intrin/futex.o: libc/intrin/futex.S
|
o/$(MODE)/libc/intrin/cosmo_futex_thunk.o: libc/intrin/cosmo_futex_thunk.S
|
||||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
o/$(MODE)/libc/intrin/typeinfo.o: libc/intrin/typeinfo.S
|
o/$(MODE)/libc/intrin/typeinfo.o: libc/intrin/typeinfo.S
|
||||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "libc/intrin/getenv.h"
|
#include "libc/intrin/getenv.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
|
|
||||||
privileged struct Env __getenv(char **p, const char *k) {
|
privileged optimizesize struct Env __getenv(char **p, const char *k) {
|
||||||
char *t;
|
char *t;
|
||||||
int i, j;
|
int i, j;
|
||||||
for (i = 0; (t = p[i]); ++i) {
|
for (i = 0; (t = p[i]); ++i) {
|
||||||
|
|
|
@ -16,155 +16,18 @@
|
||||||
│ 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/assert.h"
|
|
||||||
#include "libc/dce.h"
|
|
||||||
#include "libc/nexgen32e/nexgen32e.h"
|
|
||||||
#include "libc/nexgen32e/x86feature.h"
|
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1)));
|
|
||||||
typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16)));
|
|
||||||
|
|
||||||
static void bzero128(char *p, size_t n) {
|
|
||||||
xmm_t v = {0};
|
|
||||||
if (n <= 32) {
|
|
||||||
*(xmm_t *)(p + n - 16) = v;
|
|
||||||
*(xmm_t *)p = v;
|
|
||||||
} else {
|
|
||||||
do {
|
|
||||||
n -= 32;
|
|
||||||
*(xmm_t *)(p + n) = v;
|
|
||||||
*(xmm_t *)(p + n + 16) = v;
|
|
||||||
} while (n > 32);
|
|
||||||
*(xmm_t *)(p + 16) = v;
|
|
||||||
*(xmm_t *)p = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__x86_64__) && !defined(__chibicc__)
|
|
||||||
_Microarchitecture("avx") static void bzero_avx(char *p, size_t n) {
|
|
||||||
xmm_t v = {0};
|
|
||||||
if (n <= 32) {
|
|
||||||
*(xmm_t *)(p + n - 16) = v;
|
|
||||||
*(xmm_t *)p = v;
|
|
||||||
} else if (n >= 1024 && X86_HAVE(ERMS)) {
|
|
||||||
asm("rep stosb" : "+D"(p), "+c"(n), "=m"(*(char(*)[n])p) : "a"(0));
|
|
||||||
} else {
|
|
||||||
if (n < kHalfCache3 || !kHalfCache3) {
|
|
||||||
do {
|
|
||||||
n -= 32;
|
|
||||||
*(xmm_t *)(p + n) = v;
|
|
||||||
*(xmm_t *)(p + n + 16) = v;
|
|
||||||
} while (n > 32);
|
|
||||||
} else {
|
|
||||||
while ((uintptr_t)(p + n) & 15) {
|
|
||||||
p[--n] = 0;
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
n -= 32;
|
|
||||||
__builtin_ia32_movntdq((xmm_a *)(p + n), (xmm_a)v);
|
|
||||||
__builtin_ia32_movntdq((xmm_a *)(p + n + 16), (xmm_a)v);
|
|
||||||
} while (n > 32);
|
|
||||||
asm("sfence");
|
|
||||||
}
|
|
||||||
*(xmm_t *)(p + 16) = v;
|
|
||||||
*(xmm_t *)p = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets memory to zero.
|
* Sets memory to zero.
|
||||||
*
|
*
|
||||||
* bzero n=0 661 picoseconds
|
|
||||||
* bzero n=1 661 ps/byte 1,476 mb/s
|
|
||||||
* bzero n=2 330 ps/byte 2,952 mb/s
|
|
||||||
* bzero n=3 220 ps/byte 4,428 mb/s
|
|
||||||
* bzero n=4 165 ps/byte 5,904 mb/s
|
|
||||||
* bzero n=7 94 ps/byte 10,333 mb/s
|
|
||||||
* bzero n=8 41 ps/byte 23,618 mb/s
|
|
||||||
* bzero n=15 44 ps/byte 22,142 mb/s
|
|
||||||
* bzero n=16 20 ps/byte 47,236 mb/s
|
|
||||||
* bzero n=31 21 ps/byte 45,760 mb/s
|
|
||||||
* bzero n=32 20 ps/byte 47,236 mb/s
|
|
||||||
* bzero n=63 10 ps/byte 92,997 mb/s
|
|
||||||
* bzero n=64 15 ps/byte 62,982 mb/s
|
|
||||||
* bzero n=127 15 ps/byte 62,490 mb/s
|
|
||||||
* bzero n=128 10 ps/byte 94,473 mb/s
|
|
||||||
* bzero n=255 14 ps/byte 68,439 mb/s
|
|
||||||
* bzero n=256 9 ps/byte 105 gb/s
|
|
||||||
* bzero n=511 15 ps/byte 62,859 mb/s
|
|
||||||
* bzero n=512 11 ps/byte 83,976 mb/s
|
|
||||||
* bzero n=1023 15 ps/byte 61,636 mb/s
|
|
||||||
* bzero n=1024 10 ps/byte 88,916 mb/s
|
|
||||||
* bzero n=2047 9 ps/byte 105 gb/s
|
|
||||||
* bzero n=2048 8 ps/byte 109 gb/s
|
|
||||||
* bzero n=4095 8 ps/byte 115 gb/s
|
|
||||||
* bzero n=4096 8 ps/byte 118 gb/s
|
|
||||||
* bzero n=8191 7 ps/byte 129 gb/s
|
|
||||||
* bzero n=8192 7 ps/byte 130 gb/s
|
|
||||||
* bzero n=16383 6 ps/byte 136 gb/s
|
|
||||||
* bzero n=16384 6 ps/byte 137 gb/s
|
|
||||||
* bzero n=32767 6 ps/byte 140 gb/s
|
|
||||||
* bzero n=32768 6 ps/byte 141 gb/s
|
|
||||||
* bzero n=65535 15 ps/byte 64,257 mb/s
|
|
||||||
* bzero n=65536 15 ps/byte 64,279 mb/s
|
|
||||||
* bzero n=131071 15 ps/byte 63,166 mb/s
|
|
||||||
* bzero n=131072 15 ps/byte 63,115 mb/s
|
|
||||||
* bzero n=262143 15 ps/byte 62,052 mb/s
|
|
||||||
* bzero n=262144 15 ps/byte 62,097 mb/s
|
|
||||||
* bzero n=524287 15 ps/byte 61,699 mb/s
|
|
||||||
* bzero n=524288 15 ps/byte 61,674 mb/s
|
|
||||||
* bzero n=1048575 16 ps/byte 60,179 mb/s
|
|
||||||
* bzero n=1048576 15 ps/byte 61,330 mb/s
|
|
||||||
* bzero n=2097151 15 ps/byte 61,071 mb/s
|
|
||||||
* bzero n=2097152 15 ps/byte 61,065 mb/s
|
|
||||||
* bzero n=4194303 16 ps/byte 60,942 mb/s
|
|
||||||
* bzero n=4194304 16 ps/byte 60,947 mb/s
|
|
||||||
* bzero n=8388607 16 ps/byte 60,872 mb/s
|
|
||||||
* bzero n=8388608 16 ps/byte 60,879 mb/s
|
|
||||||
*
|
|
||||||
* @param p is memory address
|
* @param p is memory address
|
||||||
* @param n is byte length
|
* @param n is byte length
|
||||||
* @return p
|
* @return p
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
void bzero(void *p, size_t n) {
|
void bzero(void *p, size_t n) {
|
||||||
char *b;
|
memset(p, 0, n);
|
||||||
uint64_t x;
|
|
||||||
b = p;
|
|
||||||
#ifdef __x86_64__
|
|
||||||
asm("xorl\t%k0,%k0" : "=r"(x));
|
|
||||||
#else
|
|
||||||
if (1) {
|
|
||||||
memset(p, 0, n);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
x = 0;
|
|
||||||
#endif
|
|
||||||
if (n <= 16) {
|
|
||||||
if (n >= 8) {
|
|
||||||
__builtin_memcpy(b, &x, 8);
|
|
||||||
__builtin_memcpy(b + n - 8, &x, 8);
|
|
||||||
} else if (n >= 4) {
|
|
||||||
__builtin_memcpy(b, &x, 4);
|
|
||||||
__builtin_memcpy(b + n - 4, &x, 4);
|
|
||||||
} else if (n) {
|
|
||||||
do {
|
|
||||||
asm volatile("" ::: "memory");
|
|
||||||
b[--n] = x;
|
|
||||||
} while (n);
|
|
||||||
}
|
|
||||||
#if defined(__x86_64__) && !defined(__chibicc__)
|
|
||||||
} else if (IsTiny()) {
|
|
||||||
asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "a"(0));
|
|
||||||
return;
|
|
||||||
} else if (X86_HAVE(AVX)) {
|
|
||||||
bzero_avx(b, n);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
bzero128(b, n);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak_reference(bzero, explicit_bzero);
|
__weak_reference(bzero, explicit_bzero);
|
||||||
|
|
|
@ -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 = timespec_fromnanos(hectons * 100);
|
*ts = WindowsDurationToTimeSpec(hectons);
|
||||||
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 = timespec_fromnanos(hectons * 100);
|
*ts = WindowsDurationToTimeSpec(hectons);
|
||||||
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 = timespec_fromnanos(hectons * 100);
|
*ts = WindowsDurationToTimeSpec(hectons);
|
||||||
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,18 +16,15 @@
|
||||||
│ 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/sysv/consts/futex.h"
|
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/atomic.h"
|
#include "libc/atomic.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/cp.internal.h"
|
||||||
#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/state.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/calls/struct/timespec.h"
|
||||||
#include "libc/calls/struct/timespec.internal.h"
|
#include "libc/calls/struct/timespec.internal.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
|
||||||
#include "libc/cosmo.h"
|
#include "libc/cosmo.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
@ -37,62 +34,56 @@
|
||||||
#include "libc/intrin/ulock.h"
|
#include "libc/intrin/ulock.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/nexgen32e/vendor.internal.h"
|
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/runtime/clktck.h"
|
#include "libc/sysv/consts/clock.h"
|
||||||
|
#include "libc/sysv/consts/futex.h"
|
||||||
#include "libc/sysv/consts/sicode.h"
|
#include "libc/sysv/consts/sicode.h"
|
||||||
#include "libc/sysv/consts/timer.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/freebsd.internal.h"
|
#include "libc/thread/freebsd.internal.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
// clang-format off
|
||||||
#include "third_party/nsync/atomic.h"
|
|
||||||
#include "third_party/nsync/time.h"
|
|
||||||
#include "third_party/nsync/common.internal.h"
|
|
||||||
#include "third_party/nsync/futex.internal.h"
|
|
||||||
#include "third_party/nsync/time.h"
|
|
||||||
|
|
||||||
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
|
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
|
||||||
|
|
||||||
errno_t _futex (atomic_int *, int, int, const struct timespec *, int *, int);
|
errno_t cosmo_futex_thunk (atomic_int *, int, int, const struct timespec *, int *, int);
|
||||||
errno_t _futex_wake (atomic_int *, int, int) asm ("_futex");
|
errno_t _futex_wake (atomic_int *, int, int) asm ("cosmo_futex_thunk");
|
||||||
int sys_futex_cp (atomic_int *, int, int, const struct timespec *, int *, int);
|
int sys_futex_cp (atomic_int *, int, int, const struct timespec *, int *, int);
|
||||||
|
|
||||||
static struct NsyncFutex {
|
static struct CosmoFutex {
|
||||||
atomic_uint once;
|
atomic_uint once;
|
||||||
int FUTEX_WAIT_;
|
int FUTEX_WAIT_;
|
||||||
int FUTEX_PRIVATE_FLAG_;
|
int FUTEX_PRIVATE_FLAG_;
|
||||||
int FUTEX_CLOCK_REALTIME_;
|
int FUTEX_CLOCK_REALTIME_;
|
||||||
bool is_supported;
|
bool is_supported;
|
||||||
bool timeout_is_relative;
|
bool timeout_is_relative;
|
||||||
} nsync_futex_;
|
} g_cosmo_futex;
|
||||||
|
|
||||||
static void nsync_futex_init_ (void) {
|
static void cosmo_futex_init (void) {
|
||||||
int e;
|
int e;
|
||||||
atomic_int x;
|
atomic_int x;
|
||||||
|
|
||||||
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT;
|
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
|
||||||
|
|
||||||
if (IsWindows ()) {
|
if (IsWindows ()) {
|
||||||
nsync_futex_.is_supported = true;
|
g_cosmo_futex.is_supported = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsXnu ()) {
|
if (IsXnu ()) {
|
||||||
nsync_futex_.is_supported = true;
|
g_cosmo_futex.is_supported = true;
|
||||||
nsync_futex_.timeout_is_relative = true;
|
g_cosmo_futex.timeout_is_relative = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFreebsd ()) {
|
if (IsFreebsd ()) {
|
||||||
nsync_futex_.is_supported = true;
|
g_cosmo_futex.is_supported = true;
|
||||||
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nsync_futex_.is_supported = IsLinux () || IsOpenbsd ()))
|
if (!(g_cosmo_futex.is_supported = IsLinux () || IsOpenbsd ()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// In our testing, we found that the monotonic clock on various
|
// In our testing, we found that the monotonic clock on various
|
||||||
|
@ -100,7 +91,7 @@ static void nsync_futex_init_ (void) {
|
||||||
// better behaved than the realtime clock, and routinely took
|
// better behaved than the realtime clock, and routinely took
|
||||||
// large steps backwards, especially on multiprocessors. Given
|
// large steps backwards, especially on multiprocessors. Given
|
||||||
// that "monotonic" doesn't seem to mean what it says,
|
// that "monotonic" doesn't seem to mean what it says,
|
||||||
// implementers of nsync_time might consider retaining the
|
// implementers of cosmo_time might consider retaining the
|
||||||
// simplicity of a single epoch within an address space, by
|
// simplicity of a single epoch within an address space, by
|
||||||
// configuring any time synchronization mechanism (like ntp) to
|
// configuring any time synchronization mechanism (like ntp) to
|
||||||
// adjust for leap seconds by adjusting the rate, rather than
|
// adjust for leap seconds by adjusting the rate, rather than
|
||||||
|
@ -108,31 +99,32 @@ static void nsync_futex_init_ (void) {
|
||||||
e = errno;
|
e = errno;
|
||||||
atomic_store_explicit (&x, 0, memory_order_relaxed);
|
atomic_store_explicit (&x, 0, memory_order_relaxed);
|
||||||
if (IsLinux () &&
|
if (IsLinux () &&
|
||||||
_futex (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME,
|
cosmo_futex_thunk (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME,
|
||||||
1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
|
1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
|
||||||
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT_BITSET;
|
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT_BITSET;
|
||||||
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||||
nsync_futex_.FUTEX_CLOCK_REALTIME_ = FUTEX_CLOCK_REALTIME;
|
g_cosmo_futex.FUTEX_CLOCK_REALTIME_ = FUTEX_CLOCK_REALTIME;
|
||||||
} else if (IsOpenbsd () ||
|
} else if (IsOpenbsd () ||
|
||||||
(IsLinux () &&
|
(IsLinux () &&
|
||||||
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
|
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
|
||||||
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT;
|
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
|
||||||
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||||
nsync_futex_.timeout_is_relative = true;
|
g_cosmo_futex.timeout_is_relative = true;
|
||||||
} else {
|
} else {
|
||||||
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT;
|
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
|
||||||
nsync_futex_.timeout_is_relative = true;
|
g_cosmo_futex.timeout_is_relative = true;
|
||||||
}
|
}
|
||||||
errno = e;
|
errno = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t nsync_time_64to32u (uint64_t duration) {
|
static uint32_t cosmo_time_64to32u (uint64_t duration) {
|
||||||
if (duration <= -1u)
|
if (duration <= -1u)
|
||||||
return duration;
|
return duration;
|
||||||
return -1u;
|
return -1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nsync_futex_polyfill_ (atomic_int *w, int expect, int clock, struct timespec *abstime) {
|
static int cosmo_futex_polyfill (atomic_int *w, int expect, int clock,
|
||||||
|
struct timespec *abstime) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (atomic_load_explicit (w, memory_order_acquire) != expect)
|
if (atomic_load_explicit (w, memory_order_acquire) != expect)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -148,10 +140,10 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, int clock, struct t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
|
static int cosmo_futex_wait_win32 (atomic_int *w, int expect, char pshare,
|
||||||
int clock, const struct timespec *timeout,
|
int clock, const struct timespec *timeout,
|
||||||
struct PosixThread *pt,
|
struct PosixThread *pt,
|
||||||
sigset_t waitmask) {
|
sigset_t waitmask) {
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
int sig;
|
int sig;
|
||||||
bool32 ok;
|
bool32 ok;
|
||||||
|
@ -183,7 +175,7 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
|
||||||
pt->pt_blkmask = waitmask;
|
pt->pt_blkmask = waitmask;
|
||||||
atomic_store_explicit (&pt->pt_blocker, w, memory_order_release);
|
atomic_store_explicit (&pt->pt_blocker, w, memory_order_release);
|
||||||
}
|
}
|
||||||
ok = WaitOnAddress (w, &expect, sizeof(int), nsync_time_64to32u (timespec_tomillis (wait)));
|
ok = WaitOnAddress (w, &expect, sizeof(int), cosmo_time_64to32u (timespec_tomillis (wait)));
|
||||||
if (pt) {
|
if (pt) {
|
||||||
/* __sig_wake wakes our futex without changing `w` after enqueing signals */
|
/* __sig_wake wakes our futex without changing `w` after enqueing signals */
|
||||||
atomic_store_explicit (&pt->pt_blocker, 0, memory_order_release);
|
atomic_store_explicit (&pt->pt_blocker, 0, memory_order_release);
|
||||||
|
@ -197,7 +189,7 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
|
||||||
if (ok) {
|
if (ok) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
ASSERT (GetLastError () == ETIMEDOUT);
|
unassert (GetLastError () == ETIMEDOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -205,14 +197,14 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nsync_futex_fix_timeout_ (struct timespec *memory, int clock,
|
static int cosmo_futex_fix_timeout (struct timespec *memory, int clock,
|
||||||
const struct timespec *abstime,
|
const struct timespec *abstime,
|
||||||
struct timespec **result) {
|
struct timespec **result) {
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
if (!abstime) {
|
if (!abstime) {
|
||||||
*result = 0;
|
*result = 0;
|
||||||
return 0;
|
return 0;
|
||||||
} else if (!nsync_futex_.timeout_is_relative) {
|
} else if (!g_cosmo_futex.timeout_is_relative) {
|
||||||
*memory = *abstime;
|
*memory = *abstime;
|
||||||
*result = memory;
|
*result = memory;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -225,22 +217,41 @@ static int nsync_futex_fix_timeout_ (struct timespec *memory, int clock,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
|
/**
|
||||||
int clock, const struct timespec *abstime) {
|
* Waits on futex.
|
||||||
|
*
|
||||||
|
* This function may be used to ask the OS to park the calling thread
|
||||||
|
* until cosmo_futex_wake() is called on the memory address `w`.
|
||||||
|
*
|
||||||
|
* @param w is your futex
|
||||||
|
* @param expect is the value `*w` is expected to have on entry
|
||||||
|
* @param pshare is `PTHREAD_PROCESS_PRIVATE` / `PTHREAD_PROCESS_SHARED`
|
||||||
|
* @param clock is `CLOCK_MONOTONIC`, `CLOCK_REALTIME`, etc.
|
||||||
|
* @param abstime is null to wait forever or absolute timestamp to stop
|
||||||
|
* @return 0 on success, or -errno on error
|
||||||
|
* @raise EINVAL on bad parameter
|
||||||
|
* @raise EAGAIN if `*w` wasn't `expect`
|
||||||
|
* @raise EINTR if a signal handler was called while waiting
|
||||||
|
* @raise ECANCELED if calling thread was canceled while waiting
|
||||||
|
* @cancelationpoint
|
||||||
|
*/
|
||||||
|
int cosmo_futex_wait (atomic_int *w, int expect, char pshare,
|
||||||
|
int clock, const struct timespec *abstime) {
|
||||||
int e, rc, op;
|
int e, rc, op;
|
||||||
struct CosmoTib *tib;
|
struct CosmoTib *tib;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
struct timespec tsmem;
|
struct timespec tsmem;
|
||||||
struct timespec *timeout = 0;
|
struct timespec *timeout = 0;
|
||||||
|
BEGIN_CANCELATION_POINT;
|
||||||
|
|
||||||
cosmo_once (&nsync_futex_.once, nsync_futex_init_);
|
cosmo_once (&g_cosmo_futex.once, cosmo_futex_init);
|
||||||
|
|
||||||
op = nsync_futex_.FUTEX_WAIT_;
|
op = g_cosmo_futex.FUTEX_WAIT_;
|
||||||
if (pshare == PTHREAD_PROCESS_PRIVATE)
|
if (pshare == PTHREAD_PROCESS_PRIVATE)
|
||||||
op |= nsync_futex_.FUTEX_PRIVATE_FLAG_;
|
op |= g_cosmo_futex.FUTEX_PRIVATE_FLAG_;
|
||||||
if (clock == CLOCK_REALTIME ||
|
if (clock == CLOCK_REALTIME ||
|
||||||
clock == CLOCK_REALTIME_COARSE)
|
clock == CLOCK_REALTIME_COARSE)
|
||||||
op |= nsync_futex_.FUTEX_CLOCK_REALTIME_;
|
op |= g_cosmo_futex.FUTEX_CLOCK_REALTIME_;
|
||||||
|
|
||||||
if (abstime && timespec_cmp (*abstime, timespec_zero) <= 0) {
|
if (abstime && timespec_cmp (*abstime, timespec_zero) <= 0) {
|
||||||
rc = -ETIMEDOUT;
|
rc = -ETIMEDOUT;
|
||||||
|
@ -252,7 +263,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rc = nsync_futex_fix_timeout_ (&tsmem, clock, abstime, &timeout)))
|
if ((rc = cosmo_futex_fix_timeout (&tsmem, clock, abstime, &timeout)))
|
||||||
goto Finished;
|
goto Finished;
|
||||||
|
|
||||||
LOCKTRACE ("futex(%t [%d], %s, %#x, %s) → ...",
|
LOCKTRACE ("futex(%t [%d], %s, %#x, %s) → ...",
|
||||||
|
@ -263,13 +274,13 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
|
||||||
tib = __get_tls();
|
tib = __get_tls();
|
||||||
pt = (struct PosixThread *)tib->tib_pthread;
|
pt = (struct PosixThread *)tib->tib_pthread;
|
||||||
|
|
||||||
if (nsync_futex_.is_supported) {
|
if (g_cosmo_futex.is_supported) {
|
||||||
e = errno;
|
e = errno;
|
||||||
if (IsWindows ()) {
|
if (IsWindows ()) {
|
||||||
// Windows 8 futexes don't support multiple processes :(
|
// Windows 8 futexes don't support multiple processes :(
|
||||||
if (pshare) goto Polyfill;
|
if (pshare) goto Polyfill;
|
||||||
sigset_t m = __sig_block ();
|
sigset_t m = __sig_block ();
|
||||||
rc = nsync_futex_wait_win32_ (w, expect, pshare, clock, timeout, pt, m);
|
rc = cosmo_futex_wait_win32 (w, expect, pshare, clock, timeout, pt, m);
|
||||||
__sig_unblock (m);
|
__sig_unblock (m);
|
||||||
} else if (IsXnu ()) {
|
} else if (IsXnu ()) {
|
||||||
|
|
||||||
|
@ -293,7 +304,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
|
||||||
op = UL_COMPARE_AND_WAIT;
|
op = UL_COMPARE_AND_WAIT;
|
||||||
}
|
}
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
us = nsync_time_64to32u (timespec_tomicros (*timeout));
|
us = cosmo_time_64to32u (timespec_tomicros (*timeout));
|
||||||
} else {
|
} else {
|
||||||
us = -1u;
|
us = -1u;
|
||||||
}
|
}
|
||||||
|
@ -333,7 +344,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Polyfill:
|
Polyfill:
|
||||||
rc = nsync_futex_polyfill_ (w, expect, clock, timeout);
|
rc = cosmo_futex_polyfill (w, expect, clock, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
|
@ -343,21 +354,28 @@ Finished:
|
||||||
DescribeTimespec (0, abstime),
|
DescribeTimespec (0, abstime),
|
||||||
DescribeErrno (rc));
|
DescribeErrno (rc));
|
||||||
|
|
||||||
|
END_CANCELATION_POINT;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
|
/**
|
||||||
|
* Wakes futex.
|
||||||
|
*
|
||||||
|
* @param w is your futex
|
||||||
|
* @param count is number of threads to wake (usually 1 or `INT_MAX`)
|
||||||
|
* @param pshare is `PTHREAD_PROCESS_PRIVATE` / `PTHREAD_PROCESS_SHARED`
|
||||||
|
* @return number of threads woken on success, or -errno on error
|
||||||
|
*/
|
||||||
|
int cosmo_futex_wake (atomic_int *w, int count, char pshare) {
|
||||||
int rc, op, fop;
|
int rc, op, fop;
|
||||||
|
|
||||||
ASSERT (count == 1 || count == INT_MAX);
|
cosmo_once (&g_cosmo_futex.once, cosmo_futex_init);
|
||||||
|
|
||||||
cosmo_once (&nsync_futex_.once, nsync_futex_init_);
|
|
||||||
|
|
||||||
op = FUTEX_WAKE;
|
op = FUTEX_WAKE;
|
||||||
if (pshare == PTHREAD_PROCESS_PRIVATE)
|
if (pshare == PTHREAD_PROCESS_PRIVATE)
|
||||||
op |= nsync_futex_.FUTEX_PRIVATE_FLAG_;
|
op |= g_cosmo_futex.FUTEX_PRIVATE_FLAG_;
|
||||||
|
|
||||||
if (nsync_futex_.is_supported) {
|
if (g_cosmo_futex.is_supported) {
|
||||||
if (IsWindows ()) {
|
if (IsWindows ()) {
|
||||||
if (pshare) {
|
if (pshare) {
|
||||||
goto Polyfill;
|
goto Polyfill;
|
||||||
|
@ -379,7 +397,7 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
|
||||||
op |= ULF_WAKE_ALL;
|
op |= ULF_WAKE_ALL;
|
||||||
}
|
}
|
||||||
rc = ulock_wake (op, w, 0);
|
rc = ulock_wake (op, w, 0);
|
||||||
ASSERT (!rc || rc == -ENOENT);
|
unassert (!rc || rc == -ENOENT);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
rc = 1;
|
rc = 1;
|
||||||
} else if (rc == -ENOENT) {
|
} else if (rc == -ENOENT) {
|
|
@ -20,17 +20,16 @@
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
.privileged
|
.privileged
|
||||||
|
|
||||||
_futex:
|
cosmo_futex_thunk:
|
||||||
|
beg
|
||||||
|
pro
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
push %rbp
|
|
||||||
mov %rsp,%rbp
|
|
||||||
mov %rcx,%r10
|
mov %rcx,%r10
|
||||||
mov __NR_futex,%eax
|
mov __NR_futex,%eax
|
||||||
clc
|
clc
|
||||||
syscall
|
syscall
|
||||||
jnc 1f
|
jnc 1f
|
||||||
neg %eax
|
neg %eax
|
||||||
1: pop %rbp
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
ldr x7,=__hostos
|
ldr x7,=__hostos
|
||||||
ldr w7,[x7]
|
ldr w7,[x7]
|
||||||
|
@ -46,5 +45,7 @@ _futex:
|
||||||
#else
|
#else
|
||||||
#error "unsupported architecture"
|
#error "unsupported architecture"
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
1: ret
|
1: epi
|
||||||
.endfn _futex,globl,hidden
|
ret
|
||||||
|
end
|
||||||
|
.endfn cosmo_futex_thunk,globl,hidden
|
26
libc/intrin/count.c
Normal file
26
libc/intrin/count.c
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*-*- 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 2024 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/atomic.h"
|
||||||
|
#include "libc/stdalign.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
// this counter is important because pthread_exit() needs to know if
|
||||||
|
// it's an orphan thread, without needing to acquire _pthread_lock()
|
||||||
|
// which causes contention and a file descriptor explosion on netbsd
|
||||||
|
alignas(64) atomic_uint _pthread_count = 1;
|
|
@ -20,16 +20,13 @@
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/fds.h"
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
|
||||||
struct Cursor *__cursor_new(void) {
|
struct Cursor *__cursor_new(void) {
|
||||||
struct Cursor *c;
|
struct Cursor *c;
|
||||||
if ((c = _mapanon(sizeof(struct Cursor)))) {
|
if ((c = _mapanon(sizeof(struct Cursor)))) {
|
||||||
if ((c->shared = _mapshared(sizeof(struct CursorShared)))) {
|
if ((c->shared = _mapshared(sizeof(struct CursorShared)))) {
|
||||||
pthread_mutexattr_t attr;
|
c->shared->lock = (pthread_mutex_t)PTHREAD_SHARED_MUTEX_INITIALIZER_NP;
|
||||||
pthread_mutexattr_init(&attr);
|
|
||||||
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
|
||||||
pthread_mutex_init(&c->shared->lock, &attr);
|
|
||||||
pthread_mutexattr_destroy(&attr);
|
|
||||||
} else {
|
} else {
|
||||||
munmap(c, sizeof(struct Cursor));
|
munmap(c, sizeof(struct Cursor));
|
||||||
c = 0;
|
c = 0;
|
||||||
|
@ -56,9 +53,9 @@ int __cursor_unref(struct Cursor *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void __cursor_lock(struct Cursor *c) {
|
void __cursor_lock(struct Cursor *c) {
|
||||||
pthread_mutex_lock(&c->shared->lock);
|
_pthread_mutex_lock(&c->shared->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __cursor_unlock(struct Cursor *c) {
|
void __cursor_unlock(struct Cursor *c) {
|
||||||
pthread_mutex_unlock(&c->shared->lock);
|
_pthread_mutex_unlock(&c->shared->lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ struct CxaAtexitBlocks {
|
||||||
|
|
||||||
extern struct CxaAtexitBlocks __cxa_blocks;
|
extern struct CxaAtexitBlocks __cxa_blocks;
|
||||||
|
|
||||||
void __cxa_lock(void) libcesque;
|
void __cxa_lock(void) dontthrow;
|
||||||
void __cxa_unlock(void) libcesque;
|
void __cxa_unlock(void) dontthrow;
|
||||||
void __cxa_thread_finalize(void) libcesque;
|
void __cxa_thread_finalize(void) dontthrow;
|
||||||
void __cxa_printexits(FILE *, void *) libcesque;
|
void __cxa_printexits(FILE *, void *) libcesque;
|
||||||
int __cxa_thread_atexit_impl(void *, void *, void *);
|
int __cxa_thread_atexit_impl(void *, void *, void *);
|
||||||
|
|
||||||
|
|
|
@ -17,22 +17,15 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/cxaatexit.h"
|
#include "libc/intrin/cxaatexit.h"
|
||||||
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
static pthread_mutex_t __cxa_lock_obj;
|
pthread_mutex_t __cxa_lock_obj = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
void __cxa_wipe(void) {
|
|
||||||
pthread_mutex_init(&__cxa_lock_obj, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __cxa_lock(void) {
|
void __cxa_lock(void) {
|
||||||
pthread_mutex_lock(&__cxa_lock_obj);
|
_pthread_mutex_lock(&__cxa_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __cxa_unlock(void) {
|
void __cxa_unlock(void) {
|
||||||
pthread_mutex_unlock(&__cxa_lock_obj);
|
_pthread_mutex_unlock(&__cxa_lock_obj);
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((__constructor__(60))) static textstartup void __cxa_init() {
|
|
||||||
pthread_atfork(__cxa_lock, __cxa_unlock, __cxa_wipe);
|
|
||||||
}
|
}
|
||||||
|
|
277
libc/intrin/deadlock.c
Normal file
277
libc/intrin/deadlock.c
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
/*-*- 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 2024 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 "ape/sections.internal.h"
|
||||||
|
#include "libc/assert.h"
|
||||||
|
#include "libc/atomic.h"
|
||||||
|
#include "libc/cosmo.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/atomic.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/intrin/maps.h"
|
||||||
|
#include "libc/macros.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/lock.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview deadlock detector for statically allocated locks
|
||||||
|
*
|
||||||
|
* This module helps you spot multi-threading bugs in your program.
|
||||||
|
* High-level abstractions like mutexes are much easier to use than
|
||||||
|
* atomics, but they still carry their own non-obvious dangers. For
|
||||||
|
* example, nesting locks need to be nested in a consistent way and
|
||||||
|
* normal mutexes can't be required recursively. Normally this will
|
||||||
|
* cause your program to deadlock, i.e. hang indefinitely, but this
|
||||||
|
* module can detect such conditions and return errors instead, and
|
||||||
|
* better yet print helpful information when using `cosmocc -mdbg`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ABI privileged optimizesize
|
||||||
|
|
||||||
|
// building our visitor function using this optimizesize keyword shrinks
|
||||||
|
// the stack memory requirement from 7168 to 2048 bytes. totally amazing
|
||||||
|
// although please note this maximum isn't a hard limit. for normal mode
|
||||||
|
// builds your posix mandated mutex error checking will be less accurate
|
||||||
|
// but still helpful and reliable, although your cosmocc -mdbg will trap
|
||||||
|
// and report that you've run into the limit, so you can talk to justine
|
||||||
|
#define MAX_LOCKS 64
|
||||||
|
|
||||||
|
// cosmo's tib reserves space for 64 nested locks before things degrade.
|
||||||
|
// the cosmopolitan c runtime defines 16 locks, which are all registered
|
||||||
|
// with pthread_atfork(). it means you get to have 48 mutexes right now,
|
||||||
|
// and if you register all of them, then calling fork() will cause there
|
||||||
|
// to be 2080 edges in your lock graph. talk to justine if you need more
|
||||||
|
// because we're obviously going to need to find a way to make this grow
|
||||||
|
#define LOCK_EDGES_MAX 2080
|
||||||
|
|
||||||
|
// supported lock objects must define `void *_edges`
|
||||||
|
#define LOCK_EDGES_OFFSET 0
|
||||||
|
static_assert(offsetof(struct MapLock, edges) == LOCK_EDGES_OFFSET);
|
||||||
|
static_assert(offsetof(pthread_mutex_t, _edges) == LOCK_EDGES_OFFSET);
|
||||||
|
|
||||||
|
struct LockEdge {
|
||||||
|
struct LockEdge *next;
|
||||||
|
void *dest;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VisitedLock {
|
||||||
|
struct VisitedLock *next;
|
||||||
|
void *lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef _Atomic(struct LockEdge *) LockEdges;
|
||||||
|
|
||||||
|
static struct DeadlockDetector {
|
||||||
|
atomic_size_t edges_allocated;
|
||||||
|
struct LockEdge edges_memory[LOCK_EDGES_MAX];
|
||||||
|
} __deadlock;
|
||||||
|
|
||||||
|
forceinline struct CosmoTib *__deadlock_tls(void) {
|
||||||
|
return __get_tls_privileged();
|
||||||
|
}
|
||||||
|
|
||||||
|
forceinline LockEdges *get_lock_edges(void *lock) {
|
||||||
|
return (LockEdges *)((char *)lock + LOCK_EDGES_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
forceinline struct LockEdge *load_lock_edges(LockEdges *edges) {
|
||||||
|
return atomic_load_explicit(edges, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static int is_static_memory(void *lock) {
|
||||||
|
return _etext <= (unsigned char *)lock && (unsigned char *)lock < _end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static struct LockEdge *__deadlock_alloc(void) {
|
||||||
|
size_t edges_allocated =
|
||||||
|
atomic_load_explicit(&__deadlock.edges_allocated, memory_order_relaxed);
|
||||||
|
for (;;) {
|
||||||
|
if (edges_allocated == LOCK_EDGES_MAX) {
|
||||||
|
if (IsModeDbg()) {
|
||||||
|
kprintf("error: cosmo LOCK_EDGES_MAX needs to be increased\n");
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (atomic_compare_exchange_weak_explicit(
|
||||||
|
&__deadlock.edges_allocated, &edges_allocated, edges_allocated + 1,
|
||||||
|
memory_order_relaxed, memory_order_relaxed))
|
||||||
|
return &__deadlock.edges_memory[edges_allocated];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static void __deadlock_add_edge(void *from, void *dest) {
|
||||||
|
LockEdges *edges = get_lock_edges(from);
|
||||||
|
for (struct LockEdge *e = load_lock_edges(edges); e; e = e->next)
|
||||||
|
if (e->dest == dest)
|
||||||
|
return;
|
||||||
|
struct LockEdge *edge;
|
||||||
|
if ((edge = __deadlock_alloc())) {
|
||||||
|
edge->next = load_lock_edges(edges);
|
||||||
|
edge->dest = dest;
|
||||||
|
// we tolerate duplicate elements in the interest of performance.
|
||||||
|
// once an element is inserted, it's never removed. that's why we
|
||||||
|
// don't need need to worry about the aba problem. the cas itself
|
||||||
|
// is very important since it ensures inserted edges aren't lost.
|
||||||
|
for (;;)
|
||||||
|
if (atomic_compare_exchange_weak_explicit(edges, &edge->next, edge,
|
||||||
|
memory_order_relaxed,
|
||||||
|
memory_order_relaxed))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static bool __deadlock_visit(void *lock, struct VisitedLock *visited,
|
||||||
|
int notrap, int depth) {
|
||||||
|
if (++depth == MAX_LOCKS) {
|
||||||
|
if (IsModeDbg()) {
|
||||||
|
kprintf("error: too much recursion in deadlock detector\n");
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (struct VisitedLock *v = visited; v; v = v->next) {
|
||||||
|
if (v->lock == lock) {
|
||||||
|
if (IsModeDbg() && !notrap) {
|
||||||
|
// lock hierarchy violated!
|
||||||
|
//
|
||||||
|
// when you lock mutexes in a nested way, your locks must be
|
||||||
|
// nested in the same order globally. otherwise deadlocks might
|
||||||
|
// occur. for example, if you say in your first thread
|
||||||
|
//
|
||||||
|
// pthread_mutex_lock(&x);
|
||||||
|
// pthread_mutex_lock(&y);
|
||||||
|
// pthread_mutex_unlock(&y);
|
||||||
|
// pthread_mutex_unlock(&x);
|
||||||
|
//
|
||||||
|
// then in your second thread you say
|
||||||
|
//
|
||||||
|
// pthread_mutex_lock(&y);
|
||||||
|
// pthread_mutex_lock(&x);
|
||||||
|
// pthread_mutex_unlock(&x);
|
||||||
|
// pthread_mutex_unlock(&y);
|
||||||
|
//
|
||||||
|
// then a deadlock might happen, because {x→y, y→x} is cyclic!
|
||||||
|
// they don't happen often, but this is the kind of thing that
|
||||||
|
// matters if you want to build carrier grade production stuff
|
||||||
|
kprintf("error: cycle detected in directed graph of nested locks\n");
|
||||||
|
for (struct VisitedLock *v = visited; v; v = v->next)
|
||||||
|
kprintf("\t- %t\n", v->lock); // strongly connected component
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LockEdges *edges = get_lock_edges(lock);
|
||||||
|
struct VisitedLock visit = {visited, lock};
|
||||||
|
for (struct LockEdge *e = load_lock_edges(edges); e; e = e->next)
|
||||||
|
if (__deadlock_visit(e->dest, &visit, notrap, depth))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if lock is already locked by calling thread.
|
||||||
|
*
|
||||||
|
* This function may return false negatives if we run out of TLS memory.
|
||||||
|
* That suboptimal condition will be reported in debug mode.
|
||||||
|
*
|
||||||
|
* @return 1 if lock is certainly owned by calling thread, 0 if lock is
|
||||||
|
* certainly not owned by calling thread, and -1 if we're uncertain
|
||||||
|
*/
|
||||||
|
ABI int __deadlock_tracked(void *lock) {
|
||||||
|
int full = 1;
|
||||||
|
int owned = 0;
|
||||||
|
struct CosmoTib *tib = __deadlock_tls();
|
||||||
|
for (int i = 0; i < ARRAYLEN(tib->tib_locks); ++i) {
|
||||||
|
full &= tib->tib_locks[i] != NULL;
|
||||||
|
owned |= tib->tib_locks[i] == lock;
|
||||||
|
}
|
||||||
|
if (full)
|
||||||
|
return -1;
|
||||||
|
if (!owned && !is_static_memory(lock))
|
||||||
|
return -1;
|
||||||
|
return owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records that lock is held by thread.
|
||||||
|
* @param notrap can prevent error printing and debug breaking
|
||||||
|
* @asyncsignalsafe
|
||||||
|
*/
|
||||||
|
ABI void __deadlock_track(void *lock, int notrap) {
|
||||||
|
if (!notrap && !is_static_memory(lock))
|
||||||
|
return;
|
||||||
|
struct CosmoTib *tib = __deadlock_tls();
|
||||||
|
for (int i = 0; i < ARRAYLEN(tib->tib_locks); ++i) {
|
||||||
|
if (!tib->tib_locks[i]) {
|
||||||
|
tib->tib_locks[i] = lock;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsModeDbg()) {
|
||||||
|
kprintf("error: cosmo tls max lock depth needs to be increased!\n");
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records relationship for all held locks to `lock`.
|
||||||
|
* @param notrap can prevent error printing and debug breaking
|
||||||
|
* @asyncsignalsafe
|
||||||
|
*/
|
||||||
|
ABI void __deadlock_record(void *lock, int notrap) {
|
||||||
|
if (!notrap && !is_static_memory(lock))
|
||||||
|
return;
|
||||||
|
struct CosmoTib *tib = __deadlock_tls();
|
||||||
|
for (int i = 0; i < ARRAYLEN(tib->tib_locks); ++i)
|
||||||
|
if (tib->tib_locks[i] && tib->tib_locks[i] != lock)
|
||||||
|
__deadlock_add_edge(tib->tib_locks[i], lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns EDEADLK if locking `lock` could cause a deadlock.
|
||||||
|
* @param notrap can prevent error printing and debug breaking
|
||||||
|
* @asyncsignalsafe
|
||||||
|
*/
|
||||||
|
ABI int __deadlock_check(void *lock, int notrap) {
|
||||||
|
struct CosmoTib *tib = __deadlock_tls();
|
||||||
|
for (int i = 0; i < ARRAYLEN(tib->tib_locks); ++i) {
|
||||||
|
if (tib->tib_locks[i] == lock)
|
||||||
|
return 0;
|
||||||
|
if (tib->tib_locks[i]) {
|
||||||
|
struct VisitedLock visit = {0, tib->tib_locks[i]};
|
||||||
|
if (__deadlock_visit(lock, &visit, notrap, 0))
|
||||||
|
return EDEADLK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records that lock isn't held by thread.
|
||||||
|
* @asyncsignalsafe
|
||||||
|
*/
|
||||||
|
ABI void __deadlock_untrack(void *lock) {
|
||||||
|
struct CosmoTib *tib = __deadlock_tls();
|
||||||
|
for (int i = 0; i < ARRAYLEN(tib->tib_locks); ++i)
|
||||||
|
tib->tib_locks[i] = tib->tib_locks[i] != lock ? tib->tib_locks[i] : 0;
|
||||||
|
}
|
|
@ -91,6 +91,8 @@ Copyright (c) 2024 Justine Tunney <jtunney@gmail.com>");
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define ABI privileged optimizesize
|
||||||
|
|
||||||
#define DEMANGLE_NO_FLOATING_POINT
|
#define DEMANGLE_NO_FLOATING_POINT
|
||||||
|
|
||||||
#define ASSERT(x) (void)0
|
#define ASSERT(x) (void)0
|
||||||
|
@ -103,6 +105,7 @@ Copyright (c) 2024 Justine Tunney <jtunney@gmail.com>");
|
||||||
#define ELFTC_SUCCESS 1
|
#define ELFTC_SUCCESS 1
|
||||||
|
|
||||||
#define VECTOR_DEF_CAPACITY 1
|
#define VECTOR_DEF_CAPACITY 1
|
||||||
|
#define MAX_DEPTH 20
|
||||||
|
|
||||||
typedef unsigned short index_t;
|
typedef unsigned short index_t;
|
||||||
|
|
||||||
|
@ -188,6 +191,7 @@ struct demangle_data {
|
||||||
enum type_qualifier ref_qualifier_type; /* ref qualifier type */
|
enum type_qualifier ref_qualifier_type; /* ref qualifier type */
|
||||||
enum push_qualifier push_qualifier; /* which qualifiers to push */
|
enum push_qualifier push_qualifier; /* which qualifiers to push */
|
||||||
int func_type;
|
int func_type;
|
||||||
|
int depth;
|
||||||
const char *cur; /* current mangled name ptr */
|
const char *cur; /* current mangled name ptr */
|
||||||
const char *last_sname; /* last source name */
|
const char *last_sname; /* last source name */
|
||||||
intptr_t jmpbuf[5];
|
intptr_t jmpbuf[5];
|
||||||
|
@ -220,16 +224,18 @@ static int demangle_read_sname(struct demangle_data *);
|
||||||
static int demangle_read_subst(struct demangle_data *);
|
static int demangle_read_subst(struct demangle_data *);
|
||||||
static int demangle_read_type(struct demangle_data *, struct type_delimit *);
|
static int demangle_read_type(struct demangle_data *, struct type_delimit *);
|
||||||
|
|
||||||
static privileged size_t
|
ABI static size_t
|
||||||
demangle_strlen(const char *s)
|
demangle_strlen(const char *s)
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
while (*s++)
|
while (*s++) {
|
||||||
|
asm volatile("" ::: "memory");
|
||||||
++n;
|
++n;
|
||||||
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged char *
|
ABI static char *
|
||||||
demangle_stpcpy(char *d, const char *s)
|
demangle_stpcpy(char *d, const char *s)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -240,7 +246,7 @@ demangle_stpcpy(char *d, const char *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void *
|
ABI static void *
|
||||||
demangle_mempcpy(void *a, const void *b, size_t n)
|
demangle_mempcpy(void *a, const void *b, size_t n)
|
||||||
{
|
{
|
||||||
char *d = a;
|
char *d = a;
|
||||||
|
@ -250,14 +256,14 @@ demangle_mempcpy(void *a, const void *b, size_t n)
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void *
|
ABI static void *
|
||||||
demangle_memcpy(void *a, const void *b, size_t n)
|
demangle_memcpy(void *a, const void *b, size_t n)
|
||||||
{
|
{
|
||||||
demangle_mempcpy(a, b, n);
|
demangle_mempcpy(a, b, n);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_strncmp(const char *a, const char *b, size_t n)
|
demangle_strncmp(const char *a, const char *b, size_t n)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -268,7 +274,7 @@ demangle_strncmp(const char *a, const char *b, size_t n)
|
||||||
return (a[i] & 0xff) - (b[i] & 0xff);
|
return (a[i] & 0xff) - (b[i] & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_memcmp(const void *a, const void *b, size_t n)
|
demangle_memcmp(const void *a, const void *b, size_t n)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
@ -283,7 +289,7 @@ demangle_memcmp(const void *a, const void *b, size_t n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_strlcpy(char *dst, const char *src, size_t dsize)
|
demangle_strlcpy(char *dst, const char *src, size_t dsize)
|
||||||
{
|
{
|
||||||
size_t remain;
|
size_t remain;
|
||||||
|
@ -295,7 +301,7 @@ demangle_strlcpy(char *dst, const char *src, size_t dsize)
|
||||||
*dst = 0;
|
*dst = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged long
|
ABI static long
|
||||||
demangle_strtol(const char *s, int base)
|
demangle_strtol(const char *s, int base)
|
||||||
{
|
{
|
||||||
static const uint8_t demangle_base36[80] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
static const uint8_t demangle_base36[80] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||||
|
@ -312,7 +318,7 @@ demangle_strtol(const char *s, int base)
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged char *
|
ABI static char *
|
||||||
demangle_strstr(const char *haystack, const char *needle)
|
demangle_strstr(const char *haystack, const char *needle)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -333,7 +339,7 @@ demangle_strstr(const char *haystack, const char *needle)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged char *
|
ABI static char *
|
||||||
demangle_utoa(char *p, unsigned long long x)
|
demangle_utoa(char *p, unsigned long long x)
|
||||||
{
|
{
|
||||||
char t;
|
char t;
|
||||||
|
@ -354,7 +360,7 @@ demangle_utoa(char *p, unsigned long long x)
|
||||||
return p + i;
|
return p + i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged char *
|
ABI static char *
|
||||||
demangle_itoa(char *p, long long x)
|
demangle_itoa(char *p, long long x)
|
||||||
{
|
{
|
||||||
if (x < 0)
|
if (x < 0)
|
||||||
|
@ -362,7 +368,7 @@ demangle_itoa(char *p, long long x)
|
||||||
return demangle_utoa(p, x);
|
return demangle_utoa(p, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_free(struct demangle_data *h, void *ptr)
|
demangle_free(struct demangle_data *h, void *ptr)
|
||||||
{
|
{
|
||||||
index_t base;
|
index_t base;
|
||||||
|
@ -379,14 +385,17 @@ demangle_free(struct demangle_data *h, void *ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged returnspointerwithnoaliases returnsnonnull void *
|
ABI static returnspointerwithnoaliases returnsnonnull void *
|
||||||
demangle_malloc(struct demangle_data *h, int a, int n)
|
demangle_malloc(struct demangle_data *h, long a, long n)
|
||||||
{
|
{
|
||||||
int rem;
|
long rem;
|
||||||
uintptr_t ptr;
|
uintptr_t ptr;
|
||||||
index_t next, next2;
|
index_t next, next2;
|
||||||
index_t *link, *link2;
|
index_t *link, *link2;
|
||||||
int b = sizeof(index_t);
|
long b = sizeof(index_t);
|
||||||
|
|
||||||
|
if (n < 0 || n >= 32768)
|
||||||
|
__builtin_longjmp(h->jmpbuf, 1);
|
||||||
|
|
||||||
/* Roundup size. */
|
/* Roundup size. */
|
||||||
n += a - 1;
|
n += a - 1;
|
||||||
|
@ -433,7 +442,7 @@ demangle_malloc(struct demangle_data *h, int a, int n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged returnspointerwithnoaliases char *
|
ABI static returnspointerwithnoaliases char *
|
||||||
demangle_strdup(struct demangle_data *h, const char *s)
|
demangle_strdup(struct demangle_data *h, const char *s)
|
||||||
{
|
{
|
||||||
char *d = 0;
|
char *d = 0;
|
||||||
|
@ -445,7 +454,7 @@ demangle_strdup(struct demangle_data *h, const char *s)
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_vector_str_dest(struct demangle_data *h, struct vector_str *v)
|
demangle_vector_str_dest(struct demangle_data *h, struct vector_str *v)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -454,7 +463,7 @@ demangle_vector_str_dest(struct demangle_data *h, struct vector_str *v)
|
||||||
demangle_free(h, v->container);
|
demangle_free(h, v->container);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_vector_type_qualifier_dest(struct demangle_data *d,
|
demangle_vector_type_qualifier_dest(struct demangle_data *d,
|
||||||
struct vector_type_qualifier *v)
|
struct vector_type_qualifier *v)
|
||||||
{
|
{
|
||||||
|
@ -462,7 +471,7 @@ demangle_vector_type_qualifier_dest(struct demangle_data *d,
|
||||||
demangle_vector_str_dest(d, &v->ext_name);
|
demangle_vector_str_dest(d, &v->ext_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_stack_str_init(struct stack_str *ss)
|
demangle_stack_str_init(struct stack_str *ss)
|
||||||
{
|
{
|
||||||
ss->str = ss->buf;
|
ss->str = ss->buf;
|
||||||
|
@ -471,7 +480,7 @@ demangle_stack_str_init(struct stack_str *ss)
|
||||||
ss->cap = sizeof(ss->buf);
|
ss->cap = sizeof(ss->buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_stack_str_append(struct demangle_data *h, struct stack_str *ss,
|
demangle_stack_str_append(struct demangle_data *h, struct stack_str *ss,
|
||||||
const char *str, size_t len)
|
const char *str, size_t len)
|
||||||
{
|
{
|
||||||
|
@ -494,7 +503,7 @@ demangle_stack_str_append(struct demangle_data *h, struct stack_str *ss,
|
||||||
#define demangle_stack_str_append_str(h, ss, s) \
|
#define demangle_stack_str_append_str(h, ss, s) \
|
||||||
demangle_stack_str_append(h, ss, s, demangle_strlen(s))
|
demangle_stack_str_append(h, ss, s, demangle_strlen(s))
|
||||||
|
|
||||||
static privileged size_t
|
ABI static size_t
|
||||||
demangle_get_strlen_sum(struct demangle_data *h, const struct vector_str *v)
|
demangle_get_strlen_sum(struct demangle_data *h, const struct vector_str *v)
|
||||||
{
|
{
|
||||||
size_t i, len = 0;
|
size_t i, len = 0;
|
||||||
|
@ -504,7 +513,7 @@ demangle_get_strlen_sum(struct demangle_data *h, const struct vector_str *v)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_demangle_strncmp(const char *a, const char *b, size_t n)
|
demangle_demangle_strncmp(const char *a, const char *b, size_t n)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -522,7 +531,7 @@ demangle_demangle_strncmp(const char *a, const char *b, size_t n)
|
||||||
* @param l Length of the string.
|
* @param l Length of the string.
|
||||||
* @return -1 at failed, 0 at not found, 1 at found.
|
* @return -1 at failed, 0 at not found, 1 at found.
|
||||||
*/
|
*/
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_vector_str_find(struct demangle_data *h, const struct vector_str *v,
|
demangle_vector_str_find(struct demangle_data *h, const struct vector_str *v,
|
||||||
const char *o, size_t l)
|
const char *o, size_t l)
|
||||||
{
|
{
|
||||||
|
@ -546,7 +555,7 @@ demangle_vector_str_find(struct demangle_data *h, const struct vector_str *v,
|
||||||
* @param l Length of the string.
|
* @param l Length of the string.
|
||||||
* @return NULL at failed or NUL terminated new allocated string.
|
* @return NULL at failed or NUL terminated new allocated string.
|
||||||
*/
|
*/
|
||||||
static privileged char *
|
ABI static char *
|
||||||
demangle_vector_str_get_flat(struct demangle_data *ddata,
|
demangle_vector_str_get_flat(struct demangle_data *ddata,
|
||||||
const struct vector_str *v, size_t *l)
|
const struct vector_str *v, size_t *l)
|
||||||
{
|
{
|
||||||
|
@ -572,7 +581,7 @@ demangle_vector_str_get_flat(struct demangle_data *ddata,
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_vector_str_grow(struct demangle_data *ddata, struct vector_str *v)
|
demangle_vector_str_grow(struct demangle_data *ddata, struct vector_str *v)
|
||||||
{
|
{
|
||||||
size_t i, tmp_cap;
|
size_t i, tmp_cap;
|
||||||
|
@ -600,7 +609,7 @@ demangle_vector_str_grow(struct demangle_data *ddata, struct vector_str *v)
|
||||||
* @brief Initialize vector_str.
|
* @brief Initialize vector_str.
|
||||||
* @return false at failed, true at success.
|
* @return false at failed, true at success.
|
||||||
*/
|
*/
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_vector_str_init(struct demangle_data *ddata, struct vector_str *v)
|
demangle_vector_str_init(struct demangle_data *ddata, struct vector_str *v)
|
||||||
{
|
{
|
||||||
v->size = 0;
|
v->size = 0;
|
||||||
|
@ -616,7 +625,7 @@ demangle_vector_str_init(struct demangle_data *ddata, struct vector_str *v)
|
||||||
* @brief Remove last element in vector_str.
|
* @brief Remove last element in vector_str.
|
||||||
* @return false at failed, true at success.
|
* @return false at failed, true at success.
|
||||||
*/
|
*/
|
||||||
static privileged bool
|
ABI static bool
|
||||||
demangle_vector_str_pop(struct vector_str *v)
|
demangle_vector_str_pop(struct vector_str *v)
|
||||||
{
|
{
|
||||||
if (!v)
|
if (!v)
|
||||||
|
@ -636,7 +645,7 @@ demangle_vector_str_pop(struct vector_str *v)
|
||||||
* @brief Push back string to vector.
|
* @brief Push back string to vector.
|
||||||
* @return false at failed, true at success.
|
* @return false at failed, true at success.
|
||||||
*/
|
*/
|
||||||
static privileged bool
|
ABI static bool
|
||||||
demangle_vector_str_push(struct demangle_data *ddata, struct vector_str *v,
|
demangle_vector_str_push(struct demangle_data *ddata, struct vector_str *v,
|
||||||
const char *str, size_t len)
|
const char *str, size_t len)
|
||||||
{
|
{
|
||||||
|
@ -660,7 +669,7 @@ demangle_vector_str_push(struct demangle_data *ddata, struct vector_str *v,
|
||||||
* @brief Push front org vector to det vector.
|
* @brief Push front org vector to det vector.
|
||||||
* @return false at failed, true at success.
|
* @return false at failed, true at success.
|
||||||
*/
|
*/
|
||||||
static privileged bool
|
ABI static bool
|
||||||
demangle_vector_str_push_vector_head(struct demangle_data *ddata,
|
demangle_vector_str_push_vector_head(struct demangle_data *ddata,
|
||||||
struct vector_str *dst, struct vector_str *org)
|
struct vector_str *dst, struct vector_str *org)
|
||||||
{
|
{
|
||||||
|
@ -693,7 +702,7 @@ demangle_vector_str_push_vector_head(struct demangle_data *ddata,
|
||||||
* @brief Push org vector to the tail of det vector.
|
* @brief Push org vector to the tail of det vector.
|
||||||
* @return false at failed, true at success.
|
* @return false at failed, true at success.
|
||||||
*/
|
*/
|
||||||
static privileged bool
|
ABI static bool
|
||||||
demangle_vector_str_push_vector(struct demangle_data *ddata,
|
demangle_vector_str_push_vector(struct demangle_data *ddata,
|
||||||
struct vector_str *dst, struct vector_str *org)
|
struct vector_str *dst, struct vector_str *org)
|
||||||
{
|
{
|
||||||
|
@ -731,7 +740,7 @@ demangle_vector_str_push_vector(struct demangle_data *ddata,
|
||||||
* If r_len is not NULL, string length will be returned.
|
* If r_len is not NULL, string length will be returned.
|
||||||
* @return NULL at failed or NUL terminated new allocated string.
|
* @return NULL at failed or NUL terminated new allocated string.
|
||||||
*/
|
*/
|
||||||
static privileged returnspointerwithnoaliases char *
|
ABI static returnspointerwithnoaliases char *
|
||||||
demangle_vector_str_substr(struct demangle_data *ddata,
|
demangle_vector_str_substr(struct demangle_data *ddata,
|
||||||
const struct vector_str *v, size_t begin, size_t end, size_t *r_len)
|
const struct vector_str *v, size_t begin, size_t end, size_t *r_len)
|
||||||
{
|
{
|
||||||
|
@ -757,7 +766,7 @@ demangle_vector_str_substr(struct demangle_data *ddata,
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_vector_read_cmd_pop(struct vector_read_cmd *v)
|
demangle_vector_read_cmd_pop(struct vector_read_cmd *v)
|
||||||
{
|
{
|
||||||
if (!v->size)
|
if (!v->size)
|
||||||
|
@ -770,7 +779,7 @@ demangle_vector_read_cmd_pop(struct vector_read_cmd *v)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_vector_read_cmd_init(struct demangle_data *ddata,
|
demangle_vector_read_cmd_init(struct demangle_data *ddata,
|
||||||
struct vector_read_cmd *v)
|
struct vector_read_cmd *v)
|
||||||
{
|
{
|
||||||
|
@ -781,7 +790,7 @@ demangle_vector_read_cmd_init(struct demangle_data *ddata,
|
||||||
alignof(*v->r_container), sizeof(*v->r_container) * v->capacity);
|
alignof(*v->r_container), sizeof(*v->r_container) * v->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_data_init(struct demangle_data *d, const char *cur)
|
demangle_data_init(struct demangle_data *d, const char *cur)
|
||||||
{
|
{
|
||||||
demangle_vector_str_init(d, &d->output);
|
demangle_vector_str_init(d, &d->output);
|
||||||
|
@ -811,7 +820,7 @@ demangle_data_init(struct demangle_data *d, const char *cur)
|
||||||
d->last_sname = NULL;
|
d->last_sname = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_push_str(struct demangle_data *ddata, const char *str, size_t len)
|
demangle_push_str(struct demangle_data *ddata, const char *str, size_t len)
|
||||||
{
|
{
|
||||||
if (!str || !len)
|
if (!str || !len)
|
||||||
|
@ -828,7 +837,7 @@ demangle_push_str(struct demangle_data *ddata, const char *str, size_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DEMANGLE_NO_FLOATING_POINT
|
#ifndef DEMANGLE_NO_FLOATING_POINT
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_push_fp(struct demangle_data *ddata,
|
demangle_push_fp(struct demangle_data *ddata,
|
||||||
char *decoder(struct demangle_data *, const char *, size_t))
|
char *decoder(struct demangle_data *, const char *, size_t))
|
||||||
{
|
{
|
||||||
|
@ -857,13 +866,13 @@ demangle_push_fp(struct demangle_data *ddata,
|
||||||
}
|
}
|
||||||
#endif // DEMANGLE_NO_FLOATING_POINT
|
#endif // DEMANGLE_NO_FLOATING_POINT
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_pop_str(struct demangle_data *ddata)
|
demangle_pop_str(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
return demangle_vector_str_pop(ddata->cur_output);
|
return demangle_vector_str_pop(ddata->cur_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_push_subst(struct demangle_data *ddata, const char *str, size_t len)
|
demangle_push_subst(struct demangle_data *ddata, const char *str, size_t len)
|
||||||
{
|
{
|
||||||
if (!str || !len)
|
if (!str || !len)
|
||||||
|
@ -875,7 +884,7 @@ demangle_push_subst(struct demangle_data *ddata, const char *str, size_t len)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_push_subst_v(struct demangle_data *ddata, struct vector_str *v)
|
demangle_push_subst_v(struct demangle_data *ddata, struct vector_str *v)
|
||||||
{
|
{
|
||||||
int rtn;
|
int rtn;
|
||||||
|
@ -895,7 +904,7 @@ demangle_push_subst_v(struct demangle_data *ddata, struct vector_str *v)
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_push_type_qualifier(struct demangle_data *ddata,
|
demangle_push_type_qualifier(struct demangle_data *ddata,
|
||||||
struct vector_type_qualifier *v, const char *type_str)
|
struct vector_type_qualifier *v, const char *type_str)
|
||||||
{
|
{
|
||||||
|
@ -1128,7 +1137,7 @@ demangle_push_type_qualifier(struct demangle_data *ddata,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_get_subst(struct demangle_data *ddata, size_t idx)
|
demangle_get_subst(struct demangle_data *ddata, size_t idx)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -1146,7 +1155,7 @@ demangle_get_subst(struct demangle_data *ddata, size_t idx)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_get_tmpl_param(struct demangle_data *ddata, size_t idx)
|
demangle_get_tmpl_param(struct demangle_data *ddata, size_t idx)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -1163,7 +1172,7 @@ demangle_get_tmpl_param(struct demangle_data *ddata, size_t idx)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_array(struct demangle_data *ddata)
|
demangle_read_array(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
size_t i, num_len, exp_len, p_idx, idx;
|
size_t i, num_len, exp_len, p_idx, idx;
|
||||||
|
@ -1235,7 +1244,7 @@ demangle_read_array(struct demangle_data *ddata)
|
||||||
#ifndef DEMANGLE_NO_FLOATING_POINT
|
#ifndef DEMANGLE_NO_FLOATING_POINT
|
||||||
|
|
||||||
/* Simple hex to integer function used by decode_to_* function. */
|
/* Simple hex to integer function used by decode_to_* function. */
|
||||||
static privileged int
|
ABI static int
|
||||||
hex_to_dec(char c)
|
hex_to_dec(char c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
@ -1283,7 +1292,7 @@ hex_to_dec(char c)
|
||||||
* Todo
|
* Todo
|
||||||
* Replace these functions to macro.
|
* Replace these functions to macro.
|
||||||
*/
|
*/
|
||||||
static privileged returnspointerwithnoaliases char *
|
ABI static returnspointerwithnoaliases char *
|
||||||
decode_fp_to_double(struct demangle_data *ddata, const char *p, size_t len)
|
decode_fp_to_double(struct demangle_data *ddata, const char *p, size_t len)
|
||||||
{
|
{
|
||||||
double f;
|
double f;
|
||||||
|
@ -1327,7 +1336,7 @@ again:
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged returnspointerwithnoaliases char *
|
ABI static returnspointerwithnoaliases char *
|
||||||
decode_fp_to_float(struct demangle_data *ddata, const char *p, size_t len)
|
decode_fp_to_float(struct demangle_data *ddata, const char *p, size_t len)
|
||||||
{
|
{
|
||||||
size_t i, rtn_len, limit;
|
size_t i, rtn_len, limit;
|
||||||
|
@ -1369,7 +1378,7 @@ again:
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged returnspointerwithnoaliases char *
|
ABI static returnspointerwithnoaliases char *
|
||||||
decode_fp_to_long_double(struct demangle_data *ddata, const char *p, size_t len)
|
decode_fp_to_long_double(struct demangle_data *ddata, const char *p, size_t len)
|
||||||
{
|
{
|
||||||
long double f;
|
long double f;
|
||||||
|
@ -1413,7 +1422,7 @@ again:
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged returnspointerwithnoaliases char *
|
ABI static returnspointerwithnoaliases char *
|
||||||
decode_fp_to_float128(struct demangle_data *ddata, const char *p, size_t len)
|
decode_fp_to_float128(struct demangle_data *ddata, const char *p, size_t len)
|
||||||
{
|
{
|
||||||
long double f;
|
long double f;
|
||||||
|
@ -1470,7 +1479,7 @@ decode_fp_to_float128(struct demangle_data *ddata, const char *p, size_t len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged returnspointerwithnoaliases char *
|
ABI static returnspointerwithnoaliases char *
|
||||||
decode_fp_to_float80(struct demangle_data *ddata, const char *p, size_t len)
|
decode_fp_to_float80(struct demangle_data *ddata, const char *p, size_t len)
|
||||||
{
|
{
|
||||||
long double f;
|
long double f;
|
||||||
|
@ -1533,7 +1542,7 @@ decode_fp_to_float80(struct demangle_data *ddata, const char *p, size_t len)
|
||||||
|
|
||||||
#endif // DEMANGLE_NO_FLOATING_POINT
|
#endif // DEMANGLE_NO_FLOATING_POINT
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_expr_primary(struct demangle_data *ddata)
|
demangle_read_expr_primary(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
const char *num;
|
const char *num;
|
||||||
|
@ -1625,7 +1634,7 @@ demangle_read_expr_primary(struct demangle_data *ddata)
|
||||||
* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775
|
* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775
|
||||||
* http://gcc.gnu.org/viewcvs?view=rev&revision=124467
|
* http://gcc.gnu.org/viewcvs?view=rev&revision=124467
|
||||||
*/
|
*/
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_local_source_name(struct demangle_data *ddata)
|
demangle_local_source_name(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
/* L */
|
/* L */
|
||||||
|
@ -1651,7 +1660,7 @@ demangle_local_source_name(struct demangle_data *ddata)
|
||||||
* read unqualified-name, unqualified name are operator-name, ctor-dtor-name,
|
* read unqualified-name, unqualified name are operator-name, ctor-dtor-name,
|
||||||
* source-name
|
* source-name
|
||||||
*/
|
*/
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_uqname(struct demangle_data *ddata)
|
demangle_read_uqname(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -2080,7 +2089,7 @@ demangle_read_uqname(struct demangle_data *ddata)
|
||||||
* Read template parameter that forms in 'T[number]_'.
|
* Read template parameter that forms in 'T[number]_'.
|
||||||
* This function much like to read_subst but only for types.
|
* This function much like to read_subst but only for types.
|
||||||
*/
|
*/
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_tmpl_param(struct demangle_data *ddata)
|
demangle_read_tmpl_param(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
long nth;
|
long nth;
|
||||||
|
@ -2098,10 +2107,11 @@ demangle_read_tmpl_param(struct demangle_data *ddata)
|
||||||
/* T_ is first */
|
/* T_ is first */
|
||||||
++nth;
|
++nth;
|
||||||
|
|
||||||
while (*ddata->cur != '_')
|
while (*ddata->cur && *ddata->cur != '_')
|
||||||
++ddata->cur;
|
++ddata->cur;
|
||||||
|
|
||||||
ASSERT(nth > 0);
|
if (nth <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return demangle_get_tmpl_param(ddata, nth);
|
return demangle_get_tmpl_param(ddata, nth);
|
||||||
}
|
}
|
||||||
|
@ -2110,7 +2120,7 @@ demangle_read_tmpl_param(struct demangle_data *ddata)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_vector_read_cmd_push(struct demangle_data *ddata,
|
demangle_vector_read_cmd_push(struct demangle_data *ddata,
|
||||||
struct vector_read_cmd *v, enum read_cmd cmd, void *data)
|
struct vector_read_cmd *v, enum read_cmd cmd, void *data)
|
||||||
{
|
{
|
||||||
|
@ -2139,7 +2149,7 @@ demangle_vector_read_cmd_push(struct demangle_data *ddata,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_tmpl_arg(struct demangle_data *ddata)
|
demangle_read_tmpl_arg(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
if (*ddata->cur == '\0')
|
if (*ddata->cur == '\0')
|
||||||
|
@ -2158,7 +2168,7 @@ demangle_read_tmpl_arg(struct demangle_data *ddata)
|
||||||
return demangle_read_type(ddata, NULL);
|
return demangle_read_type(ddata, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_tmpl_args(struct demangle_data *ddata)
|
demangle_read_tmpl_args(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
struct vector_str *v;
|
struct vector_str *v;
|
||||||
|
@ -2211,7 +2221,7 @@ demangle_read_tmpl_args(struct demangle_data *ddata)
|
||||||
return demangle_vector_read_cmd_pop(&ddata->cmd);
|
return demangle_vector_read_cmd_pop(&ddata->cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_expression_trinary(struct demangle_data *ddata, const char *name1,
|
demangle_read_expression_trinary(struct demangle_data *ddata, const char *name1,
|
||||||
size_t len1, const char *name2, size_t len2)
|
size_t len1, const char *name2, size_t len2)
|
||||||
{
|
{
|
||||||
|
@ -2230,7 +2240,7 @@ demangle_read_expression_trinary(struct demangle_data *ddata, const char *name1,
|
||||||
return demangle_read_expression(ddata);
|
return demangle_read_expression(ddata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_expression_unary(struct demangle_data *ddata, const char *name,
|
demangle_read_expression_unary(struct demangle_data *ddata, const char *name,
|
||||||
size_t len)
|
size_t len)
|
||||||
{
|
{
|
||||||
|
@ -2242,7 +2252,7 @@ demangle_read_expression_unary(struct demangle_data *ddata, const char *name,
|
||||||
return demangle_push_str(ddata, name, len);
|
return demangle_push_str(ddata, name, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_expression_binary(struct demangle_data *ddata, const char *name,
|
demangle_read_expression_binary(struct demangle_data *ddata, const char *name,
|
||||||
size_t len)
|
size_t len)
|
||||||
{
|
{
|
||||||
|
@ -2256,8 +2266,8 @@ demangle_read_expression_binary(struct demangle_data *ddata, const char *name,
|
||||||
return demangle_read_expression(ddata);
|
return demangle_read_expression(ddata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_expression(struct demangle_data *ddata)
|
demangle_read_expression_impl(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
if (*ddata->cur == '\0')
|
if (*ddata->cur == '\0')
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2538,7 +2548,18 @@ demangle_read_expression(struct demangle_data *ddata)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
|
demangle_read_expression(struct demangle_data *ddata)
|
||||||
|
{
|
||||||
|
if (ddata->depth == MAX_DEPTH)
|
||||||
|
__builtin_longjmp(ddata->jmpbuf, 1);
|
||||||
|
++ddata->depth;
|
||||||
|
int res = demangle_read_expression_impl(ddata);
|
||||||
|
--ddata->depth;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static int
|
||||||
demangle_read_expression_flat(struct demangle_data *ddata, char **str)
|
demangle_read_expression_flat(struct demangle_data *ddata, char **str)
|
||||||
{
|
{
|
||||||
struct vector_str *output;
|
struct vector_str *output;
|
||||||
|
@ -2567,7 +2588,7 @@ demangle_read_expression_flat(struct demangle_data *ddata, char **str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* size, capacity, ext_name */
|
/* size, capacity, ext_name */
|
||||||
static privileged void
|
ABI static void
|
||||||
demangle_vector_type_qualifier_init(struct demangle_data *ddata,
|
demangle_vector_type_qualifier_init(struct demangle_data *ddata,
|
||||||
struct vector_type_qualifier *v)
|
struct vector_type_qualifier *v)
|
||||||
{
|
{
|
||||||
|
@ -2583,7 +2604,7 @@ demangle_vector_type_qualifier_init(struct demangle_data *ddata,
|
||||||
demangle_vector_str_init(ddata, &v->ext_name);
|
demangle_vector_str_init(ddata, &v->ext_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged struct read_cmd_item *
|
ABI static struct read_cmd_item *
|
||||||
demangle_vector_read_cmd_find(struct vector_read_cmd *v, enum read_cmd dst)
|
demangle_vector_read_cmd_find(struct vector_read_cmd *v, enum read_cmd dst)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -2598,7 +2619,7 @@ demangle_vector_read_cmd_find(struct vector_read_cmd *v, enum read_cmd dst)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_function(struct demangle_data *ddata, int *ext_c,
|
demangle_read_function(struct demangle_data *ddata, int *ext_c,
|
||||||
struct vector_type_qualifier *v)
|
struct vector_type_qualifier *v)
|
||||||
{
|
{
|
||||||
|
@ -2734,7 +2755,7 @@ demangle_read_function(struct demangle_data *ddata, int *ext_c,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_offset_number(struct demangle_data *ddata)
|
demangle_read_offset_number(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
bool negative;
|
bool negative;
|
||||||
|
@ -2752,7 +2773,7 @@ demangle_read_offset_number(struct demangle_data *ddata)
|
||||||
start = ddata->cur;
|
start = ddata->cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (*ddata->cur != '_')
|
while (*ddata->cur && *ddata->cur != '_')
|
||||||
++ddata->cur;
|
++ddata->cur;
|
||||||
|
|
||||||
if (negative && !DEM_PUSH_STR(ddata, "-"))
|
if (negative && !DEM_PUSH_STR(ddata, "-"))
|
||||||
|
@ -2770,7 +2791,7 @@ demangle_read_offset_number(struct demangle_data *ddata)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_nv_offset(struct demangle_data *ddata)
|
demangle_read_nv_offset(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
if (!DEM_PUSH_STR(ddata, "offset : "))
|
if (!DEM_PUSH_STR(ddata, "offset : "))
|
||||||
|
@ -2779,7 +2800,7 @@ demangle_read_nv_offset(struct demangle_data *ddata)
|
||||||
return demangle_read_offset_number(ddata);
|
return demangle_read_offset_number(ddata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_v_offset(struct demangle_data *ddata)
|
demangle_read_v_offset(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
if (!DEM_PUSH_STR(ddata, "offset : "))
|
if (!DEM_PUSH_STR(ddata, "offset : "))
|
||||||
|
@ -2795,7 +2816,7 @@ demangle_read_v_offset(struct demangle_data *ddata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read offset, offset are nv-offset, v-offset */
|
/* read offset, offset are nv-offset, v-offset */
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_offset(struct demangle_data *ddata)
|
demangle_read_offset(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
if (*ddata->cur == 'h') {
|
if (*ddata->cur == 'h') {
|
||||||
|
@ -2809,7 +2830,7 @@ demangle_read_offset(struct demangle_data *ddata)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_type_flat(struct demangle_data *ddata, char **str)
|
demangle_read_type_flat(struct demangle_data *ddata, char **str)
|
||||||
{
|
{
|
||||||
struct vector_str *output;
|
struct vector_str *output;
|
||||||
|
@ -2841,7 +2862,7 @@ demangle_read_type_flat(struct demangle_data *ddata, char **str)
|
||||||
* read number
|
* read number
|
||||||
* number ::= [n] <decimal>
|
* number ::= [n] <decimal>
|
||||||
*/
|
*/
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_number(struct demangle_data *ddata, long *rtn)
|
demangle_read_number(struct demangle_data *ddata, long *rtn)
|
||||||
{
|
{
|
||||||
long len, negative_factor;
|
long len, negative_factor;
|
||||||
|
@ -2859,19 +2880,18 @@ demangle_read_number(struct demangle_data *ddata, long *rtn)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
len = demangle_strtol(ddata->cur, 10);
|
len = demangle_strtol(ddata->cur, 10);
|
||||||
|
if (len < 0)
|
||||||
|
__builtin_longjmp(ddata->jmpbuf, 1);
|
||||||
|
|
||||||
while (ELFTC_ISDIGIT(*ddata->cur))
|
while (ELFTC_ISDIGIT(*ddata->cur))
|
||||||
++ddata->cur;
|
++ddata->cur;
|
||||||
|
|
||||||
ASSERT(len >= 0);
|
|
||||||
ASSERT(negative_factor == 1 || negative_factor == -1);
|
|
||||||
|
|
||||||
*rtn = len * negative_factor;
|
*rtn = len * negative_factor;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_number_as_string(struct demangle_data *ddata, char **str)
|
demangle_read_number_as_string(struct demangle_data *ddata, char **str)
|
||||||
{
|
{
|
||||||
long n;
|
long n;
|
||||||
|
@ -2888,9 +2908,8 @@ demangle_read_number_as_string(struct demangle_data *ddata, char **str)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read encoding, encoding are function name, data name, special-name */
|
ABI static int
|
||||||
static privileged int
|
demangle_read_encoding_impl(struct demangle_data *ddata)
|
||||||
demangle_read_encoding(struct demangle_data *ddata)
|
|
||||||
{
|
{
|
||||||
char *name, *type, *num_str;
|
char *name, *type, *num_str;
|
||||||
long offset;
|
long offset;
|
||||||
|
@ -3097,7 +3116,19 @@ demangle_read_encoding(struct demangle_data *ddata)
|
||||||
return demangle_read_name(ddata);
|
return demangle_read_name(ddata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
/* read encoding, encoding are function name, data name, special-name */
|
||||||
|
ABI static int
|
||||||
|
demangle_read_encoding(struct demangle_data *ddata)
|
||||||
|
{
|
||||||
|
if (ddata->depth == MAX_DEPTH)
|
||||||
|
__builtin_longjmp(ddata->jmpbuf, 1);
|
||||||
|
++ddata->depth;
|
||||||
|
int res = demangle_read_encoding_impl(ddata);
|
||||||
|
--ddata->depth;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static int
|
||||||
demangle_read_local_name(struct demangle_data *ddata)
|
demangle_read_local_name(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
struct vector_str local_name;
|
struct vector_str local_name;
|
||||||
|
@ -3178,7 +3209,7 @@ demangle_read_local_name(struct demangle_data *ddata)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_nested_name(struct demangle_data *ddata)
|
demangle_read_nested_name(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
struct stack_str v;
|
struct stack_str v;
|
||||||
|
@ -3266,8 +3297,8 @@ next:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_name(struct demangle_data *ddata)
|
demangle_read_name_impl(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
struct stack_str v;
|
struct stack_str v;
|
||||||
struct vector_str *output;
|
struct vector_str *output;
|
||||||
|
@ -3328,7 +3359,18 @@ clean:
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
|
demangle_read_name(struct demangle_data *ddata)
|
||||||
|
{
|
||||||
|
if (ddata->depth == MAX_DEPTH)
|
||||||
|
__builtin_longjmp(ddata->jmpbuf, 1);
|
||||||
|
++ddata->depth;
|
||||||
|
int res = demangle_read_name_impl(ddata);
|
||||||
|
--ddata->depth;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static int
|
||||||
demangle_read_name_flat(struct demangle_data *ddata, char **str)
|
demangle_read_name_flat(struct demangle_data *ddata, char **str)
|
||||||
{
|
{
|
||||||
struct vector_str *output;
|
struct vector_str *output;
|
||||||
|
@ -3356,7 +3398,7 @@ demangle_read_name_flat(struct demangle_data *ddata, char **str)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_pointer_to_member(struct demangle_data *ddata,
|
demangle_read_pointer_to_member(struct demangle_data *ddata,
|
||||||
struct vector_type_qualifier *v)
|
struct vector_type_qualifier *v)
|
||||||
{
|
{
|
||||||
|
@ -3416,9 +3458,10 @@ clean1:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read source-name, source-name is <len> <ID> */
|
/* read source-name, source-name is <len> <ID> */
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_sname(struct demangle_data *ddata)
|
demangle_read_sname(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
|
size_t lim;
|
||||||
long len;
|
long len;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -3438,12 +3481,15 @@ demangle_read_sname(struct demangle_data *ddata)
|
||||||
ddata->last_sname = VEC_STR(ddata, ddata->cur_output,
|
ddata->last_sname = VEC_STR(ddata, ddata->cur_output,
|
||||||
ddata->cur_output->size - 1);
|
ddata->cur_output->size - 1);
|
||||||
|
|
||||||
|
lim = demangle_strlen(ddata->cur);
|
||||||
|
if (len > lim)
|
||||||
|
len = lim;
|
||||||
ddata->cur += len;
|
ddata->cur += len;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_subst_stdtmpl(struct demangle_data *ddata, const char *str)
|
demangle_read_subst_stdtmpl(struct demangle_data *ddata, const char *str)
|
||||||
{
|
{
|
||||||
struct vector_str *output;
|
struct vector_str *output;
|
||||||
|
@ -3481,7 +3527,7 @@ demangle_read_subst_stdtmpl(struct demangle_data *ddata, const char *str)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_subst_std(struct demangle_data *ddata)
|
demangle_read_subst_std(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
struct vector_str *output, v;
|
struct vector_str *output, v;
|
||||||
|
@ -3532,7 +3578,7 @@ demangle_read_subst_std(struct demangle_data *ddata)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_subst(struct demangle_data *ddata)
|
demangle_read_subst(struct demangle_data *ddata)
|
||||||
{
|
{
|
||||||
long nth;
|
long nth;
|
||||||
|
@ -3647,10 +3693,11 @@ demangle_read_subst(struct demangle_data *ddata)
|
||||||
/* first was '_', so increase one */
|
/* first was '_', so increase one */
|
||||||
++nth;
|
++nth;
|
||||||
|
|
||||||
while (*ddata->cur != '_')
|
while (*ddata->cur && *ddata->cur != '_')
|
||||||
++ddata->cur;
|
++ddata->cur;
|
||||||
|
|
||||||
ASSERT(nth > 0);
|
if (nth <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return demangle_get_subst(ddata, nth);
|
return demangle_get_subst(ddata, nth);
|
||||||
}
|
}
|
||||||
|
@ -3659,7 +3706,7 @@ demangle_read_subst(struct demangle_data *ddata)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_vector_type_qualifier_push(struct demangle_data *ddata,
|
demangle_vector_type_qualifier_push(struct demangle_data *ddata,
|
||||||
struct vector_type_qualifier *v, enum type_qualifier t)
|
struct vector_type_qualifier *v, enum type_qualifier t)
|
||||||
{
|
{
|
||||||
|
@ -3688,8 +3735,8 @@ demangle_vector_type_qualifier_push(struct demangle_data *ddata,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_read_type(struct demangle_data *ddata, struct type_delimit *td)
|
demangle_read_type_impl(struct demangle_data *ddata, struct type_delimit *td)
|
||||||
{
|
{
|
||||||
struct vector_type_qualifier v;
|
struct vector_type_qualifier v;
|
||||||
struct vector_str *output, sv;
|
struct vector_str *output, sv;
|
||||||
|
@ -3881,7 +3928,7 @@ again:
|
||||||
|
|
||||||
case 'E':
|
case 'E':
|
||||||
/* unexpected end (except some things) */
|
/* unexpected end (except some things) */
|
||||||
if (ddata->is_guard_variable)
|
if (td && ddata->is_guard_variable)
|
||||||
td->paren = false;
|
td->paren = false;
|
||||||
if (ddata->is_guard_variable ||
|
if (ddata->is_guard_variable ||
|
||||||
(ddata->ref_qualifier && ddata->is_functype)) {
|
(ddata->ref_qualifier && ddata->is_functype)) {
|
||||||
|
@ -4102,6 +4149,8 @@ again:
|
||||||
if (!demangle_vector_str_push(ddata, &v.ext_name, ddata->cur,
|
if (!demangle_vector_str_push(ddata, &v.ext_name, ddata->cur,
|
||||||
len))
|
len))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (len > demangle_strlen(ddata->cur))
|
||||||
|
len = demangle_strlen(ddata->cur);
|
||||||
ddata->cur += len;
|
ddata->cur += len;
|
||||||
if (!demangle_vector_type_qualifier_push(ddata, &v, TYPE_EXT))
|
if (!demangle_vector_type_qualifier_push(ddata, &v, TYPE_EXT))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4209,7 +4258,18 @@ clean:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
|
demangle_read_type(struct demangle_data *ddata, struct type_delimit *td)
|
||||||
|
{
|
||||||
|
if (ddata->depth == MAX_DEPTH)
|
||||||
|
__builtin_longjmp(ddata->jmpbuf, 1);
|
||||||
|
++ddata->depth;
|
||||||
|
int res = demangle_read_type_impl(ddata, td);
|
||||||
|
--ddata->depth;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI static int
|
||||||
demangle_copy_output(struct demangle_data *ddata, char *buf,
|
demangle_copy_output(struct demangle_data *ddata, char *buf,
|
||||||
const struct vector_str *v, size_t buflen)
|
const struct vector_str *v, size_t buflen)
|
||||||
{
|
{
|
||||||
|
@ -4232,14 +4292,14 @@ demangle_copy_output(struct demangle_data *ddata, char *buf,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle_failure(char *buf, const char *org, size_t buflen)
|
demangle_failure(char *buf, const char *org, size_t buflen)
|
||||||
{
|
{
|
||||||
demangle_strlcpy(buf, org, buflen);
|
demangle_strlcpy(buf, org, buflen);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int
|
ABI static int
|
||||||
demangle(struct demangle_data *ddata, char *buf, const char *org, size_t buflen)
|
demangle(struct demangle_data *ddata, char *buf, const char *org, size_t buflen)
|
||||||
{
|
{
|
||||||
struct vector_str ret_type;
|
struct vector_str ret_type;
|
||||||
|
@ -4391,7 +4451,7 @@ demangle(struct demangle_data *ddata, char *buf, const char *org, size_t buflen)
|
||||||
* @return bytes of output name or -1 upon error or truncation
|
* @return bytes of output name or -1 upon error or truncation
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
privileged int
|
ABI int
|
||||||
__demangle(char *buf, const char *org, size_t buflen)
|
__demangle(char *buf, const char *org, size_t buflen)
|
||||||
{
|
{
|
||||||
struct demangle_data ddata[1];
|
struct demangle_data ddata[1];
|
||||||
|
@ -4405,7 +4465,7 @@ __demangle(char *buf, const char *org, size_t buflen)
|
||||||
*
|
*
|
||||||
* This means it starts with either "_Z" or "_GLOBAL__I_".
|
* This means it starts with either "_Z" or "_GLOBAL__I_".
|
||||||
*/
|
*/
|
||||||
privileged int
|
ABI int
|
||||||
__is_mangled(const char *org)
|
__is_mangled(const char *org)
|
||||||
{
|
{
|
||||||
if (!org)
|
if (!org)
|
||||||
|
|
32
libc/intrin/describeallocationtype.c
Normal file
32
libc/intrin/describeallocationtype.c
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*-*- 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 2024 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/intrin/describeflags.h"
|
||||||
|
#include "libc/macros.h"
|
||||||
|
#include "libc/nt/enum/memflags.h"
|
||||||
|
|
||||||
|
static const struct DescribeFlags kNtAllocationTypeFlags[] = {
|
||||||
|
{kNtMemCommit, "Commit"}, //
|
||||||
|
{kNtMemReserve, "Reserve"}, //
|
||||||
|
{kNtMemReset, "Reset"}, //
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *_DescribeNtAllocationType(char buf[48], uint32_t x) {
|
||||||
|
return _DescribeFlags(buf, 48, kNtAllocationTypeFlags,
|
||||||
|
ARRAYLEN(kNtAllocationTypeFlags), "kNtMem", x);
|
||||||
|
}
|
|
@ -24,13 +24,15 @@
|
||||||
|
|
||||||
#define N 160
|
#define N 160
|
||||||
|
|
||||||
privileged static bool IsDangerous(const void *ptr) {
|
#define ABI privileged optimizesize
|
||||||
|
|
||||||
|
ABI static bool IsDangerous(const void *ptr) {
|
||||||
if (_weaken(kisdangerous))
|
if (_weaken(kisdangerous))
|
||||||
return _weaken(kisdangerous)(ptr);
|
return _weaken(kisdangerous)(ptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged static char *FormatHex(char *p, unsigned long x) {
|
ABI static char *FormatHex(char *p, unsigned long x) {
|
||||||
int k = x ? (__builtin_clzl(x) ^ 63) + 1 : 1;
|
int k = x ? (__builtin_clzl(x) ^ 63) + 1 : 1;
|
||||||
k = (k + 3) & -4;
|
k = (k + 3) & -4;
|
||||||
while (k > 0)
|
while (k > 0)
|
||||||
|
@ -39,8 +41,7 @@ privileged static char *FormatHex(char *p, unsigned long x) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged dontinstrument const char *_DescribeBacktrace(
|
ABI const char *_DescribeBacktrace(char buf[N], const struct StackFrame *fr) {
|
||||||
char buf[N], const struct StackFrame *fr) {
|
|
||||||
char *p = buf;
|
char *p = buf;
|
||||||
char *pe = p + N;
|
char *pe = p + N;
|
||||||
bool gotsome = false;
|
bool gotsome = false;
|
||||||
|
|
|
@ -29,6 +29,7 @@ const char *_DescribeMapping(char[8], int, int) libcesque;
|
||||||
const char *_DescribeMremapFlags(char[30], int) libcesque;
|
const char *_DescribeMremapFlags(char[30], int) libcesque;
|
||||||
const char *_DescribeMsg(char[16], int) libcesque;
|
const char *_DescribeMsg(char[16], int) libcesque;
|
||||||
const char *_DescribeMsyncFlags(char[48], int) libcesque;
|
const char *_DescribeMsyncFlags(char[48], int) libcesque;
|
||||||
|
const char *_DescribeNtAllocationType(char[48], uint32_t);
|
||||||
const char *_DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
|
const char *_DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
|
||||||
const char *_DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
|
const char *_DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
|
||||||
const char *_DescribeNtCreationDisposition(uint32_t) libcesque;
|
const char *_DescribeNtCreationDisposition(uint32_t) libcesque;
|
||||||
|
@ -87,6 +88,7 @@ const char *_DescribeWhichPrio(char[12], int) libcesque;
|
||||||
#define DescribeMremapFlags(x) _DescribeMremapFlags(alloca(30), x)
|
#define DescribeMremapFlags(x) _DescribeMremapFlags(alloca(30), x)
|
||||||
#define DescribeMsg(x) _DescribeMsg(alloca(16), x)
|
#define DescribeMsg(x) _DescribeMsg(alloca(16), x)
|
||||||
#define DescribeMsyncFlags(x) _DescribeMsyncFlags(alloca(48), x)
|
#define DescribeMsyncFlags(x) _DescribeMsyncFlags(alloca(48), x)
|
||||||
|
#define DescribeNtAllocationType(x) _DescribeNtAllocationType(alloca(48), x)
|
||||||
#define DescribeNtConsoleInFlags(x) _DescribeNtConsoleInFlags(alloca(256), x)
|
#define DescribeNtConsoleInFlags(x) _DescribeNtConsoleInFlags(alloca(256), x)
|
||||||
#define DescribeNtConsoleOutFlags(x) _DescribeNtConsoleOutFlags(alloca(128), x)
|
#define DescribeNtConsoleOutFlags(x) _DescribeNtConsoleOutFlags(alloca(128), x)
|
||||||
#define DescribeNtFileAccessFlags(x) _DescribeNtFileAccessFlags(alloca(512), x)
|
#define DescribeNtFileAccessFlags(x) _DescribeNtFileAccessFlags(alloca(512), x)
|
||||||
|
|
|
@ -16,25 +16,29 @@
|
||||||
│ 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/dce.h"
|
||||||
#include "libc/intrin/describeflags.h"
|
#include "libc/intrin/describeflags.h"
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/nt/enum/consolemodeflags.h"
|
#include "libc/nt/enum/consolemodeflags.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
|
||||||
|
#define MAP_GROWSDOWN_LINUX 0x00000100
|
||||||
|
|
||||||
const char *_DescribeMapFlags(char buf[64], int x) {
|
const char *_DescribeMapFlags(char buf[64], int x) {
|
||||||
const struct DescribeFlags kMapFlags[] = {
|
const struct DescribeFlags kMapFlags[] = {
|
||||||
{MAP_PRIVATE, "PRIVATE"}, //
|
{MAP_PRIVATE, "PRIVATE"}, //
|
||||||
{MAP_ANONYMOUS, "ANONYMOUS"}, //
|
{MAP_ANONYMOUS, "ANONYMOUS"}, //
|
||||||
{MAP_SHARED, "SHARED"}, //
|
{MAP_SHARED, "SHARED"}, //
|
||||||
{MAP_FIXED, "FIXED"}, //
|
{MAP_FIXED, "FIXED"}, //
|
||||||
{MAP_FIXED_NOREPLACE, "FIXED_NOREPLACE"}, //
|
{MAP_FIXED_NOREPLACE, "FIXED_NOREPLACE"}, //
|
||||||
{MAP_HUGETLB, "HUGETLB"}, //
|
{MAP_HUGETLB, "HUGETLB"}, //
|
||||||
{MAP_CONCEAL, "CONCEAL"}, //
|
{MAP_CONCEAL, "CONCEAL"}, //
|
||||||
{MAP_LOCKED, "LOCKED"}, //
|
{MAP_LOCKED, "LOCKED"}, //
|
||||||
{MAP_NORESERVE, "NORESERVE"}, //
|
{MAP_NORESERVE, "NORESERVE"}, //
|
||||||
{MAP_NONBLOCK, "NONBLOCK"}, //
|
{MAP_NONBLOCK, "NONBLOCK"}, //
|
||||||
{MAP_POPULATE, "POPULATE"}, //
|
{MAP_POPULATE, "POPULATE"}, //
|
||||||
|
{IsLinux() ? MAP_GROWSDOWN_LINUX : 0, "GROWSDOWN"}, //
|
||||||
};
|
};
|
||||||
return _DescribeFlags(buf, 64, kMapFlags, ARRAYLEN(kMapFlags), "MAP_", x);
|
return _DescribeFlags(buf, 64, kMapFlags, ARRAYLEN(kMapFlags), "MAP_", x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/describeflags.h"
|
#include "libc/intrin/describeflags.h"
|
||||||
|
#include "libc/intrin/maps.h"
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
@ -24,12 +25,13 @@
|
||||||
static char DescribeMapType(int flags) {
|
static char DescribeMapType(int flags) {
|
||||||
switch (flags & MAP_TYPE) {
|
switch (flags & MAP_TYPE) {
|
||||||
case MAP_FILE:
|
case MAP_FILE:
|
||||||
|
if (flags & MAP_NOFORK)
|
||||||
|
return 'i'; // executable image
|
||||||
return '-';
|
return '-';
|
||||||
case MAP_PRIVATE:
|
case MAP_PRIVATE:
|
||||||
if (flags & MAP_NOFORK)
|
if (flags & MAP_NOFORK)
|
||||||
return 'P';
|
return 'w'; // windows memory
|
||||||
else
|
return 'p';
|
||||||
return 'p';
|
|
||||||
case MAP_SHARED:
|
case MAP_SHARED:
|
||||||
return 's';
|
return 's';
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
|
||||||
const char *_DescribeProtFlags(char buf[48], int x) {
|
const char *_DescribeProtFlags(char buf[48], int x) {
|
||||||
|
if (!x)
|
||||||
|
return "PROT_NONE";
|
||||||
const struct DescribeFlags kProtFlags[] = {
|
const struct DescribeFlags kProtFlags[] = {
|
||||||
{PROT_READ, "READ"}, //
|
{PROT_READ, "READ"}, //
|
||||||
{PROT_WRITE, "WRITE"}, //
|
{PROT_WRITE, "WRITE"}, //
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/metalfile.internal.h"
|
#include "libc/calls/metalfile.internal.h"
|
||||||
#include "libc/intrin/directmap.h"
|
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/runtime/pc.internal.h"
|
#include "libc/runtime/pc.internal.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
@ -32,19 +31,11 @@
|
||||||
|
|
||||||
static uint64_t sys_mmap_metal_break;
|
static uint64_t sys_mmap_metal_break;
|
||||||
|
|
||||||
static struct DirectMap bad_mmap(void) {
|
void *sys_mmap_metal(void *vaddr, size_t size, int prot, int flags, int fd,
|
||||||
struct DirectMap res;
|
int64_t off) {
|
||||||
res.addr = (void *)-1;
|
|
||||||
res.maphandle = -1;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot, int flags,
|
|
||||||
int fd, int64_t off) {
|
|
||||||
/* asan runtime depends on this function */
|
/* asan runtime depends on this function */
|
||||||
size_t i;
|
size_t i;
|
||||||
struct mman *mm;
|
struct mman *mm;
|
||||||
struct DirectMap res;
|
|
||||||
uint64_t addr, faddr = 0, page, e, *pte, *fdpte, *pml4t;
|
uint64_t addr, faddr = 0, page, e, *pte, *fdpte, *pml4t;
|
||||||
mm = __get_mm();
|
mm = __get_mm();
|
||||||
pml4t = __get_pml4t();
|
pml4t = __get_pml4t();
|
||||||
|
@ -54,18 +45,18 @@ struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot, int flags,
|
||||||
struct Fd *sfd;
|
struct Fd *sfd;
|
||||||
struct MetalFile *file;
|
struct MetalFile *file;
|
||||||
if (off < 0 || fd < 0 || fd >= g_fds.n)
|
if (off < 0 || fd < 0 || fd >= g_fds.n)
|
||||||
return bad_mmap();
|
return MAP_FAILED;
|
||||||
sfd = &g_fds.p[fd];
|
sfd = &g_fds.p[fd];
|
||||||
if (sfd->kind != kFdFile)
|
if (sfd->kind != kFdFile)
|
||||||
return bad_mmap();
|
return MAP_FAILED;
|
||||||
file = (struct MetalFile *)sfd->handle;
|
file = (struct MetalFile *)sfd->handle;
|
||||||
/* TODO: allow mapping partial page at end of file, if file size not
|
/* TODO: allow mapping partial page at end of file, if file size not
|
||||||
* multiple of page size */
|
* multiple of page size */
|
||||||
if (off > file->size || size > file->size - off)
|
if (off > file->size || size > file->size - off)
|
||||||
return bad_mmap();
|
return MAP_FAILED;
|
||||||
faddr = (uint64_t)file->base + off;
|
faddr = (uint64_t)file->base + off;
|
||||||
if (faddr % 4096 != 0)
|
if (faddr % 4096 != 0)
|
||||||
return bad_mmap();
|
return MAP_FAILED;
|
||||||
}
|
}
|
||||||
if (!(flags & MAP_FIXED_linux)) {
|
if (!(flags & MAP_FIXED_linux)) {
|
||||||
if (!addr) {
|
if (!addr) {
|
||||||
|
@ -88,7 +79,7 @@ struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot, int flags,
|
||||||
if ((flags & MAP_ANONYMOUS_linux)) {
|
if ((flags & MAP_ANONYMOUS_linux)) {
|
||||||
page = __new_page(mm);
|
page = __new_page(mm);
|
||||||
if (!page)
|
if (!page)
|
||||||
return bad_mmap();
|
return MAP_FAILED;
|
||||||
__clear_page(BANE + page);
|
__clear_page(BANE + page);
|
||||||
e = page | PAGE_RSRV | PAGE_U;
|
e = page | PAGE_RSRV | PAGE_U;
|
||||||
if ((prot & PROT_WRITE))
|
if ((prot & PROT_WRITE))
|
||||||
|
@ -114,9 +105,7 @@ struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot, int flags,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.addr = (void *)addr;
|
return (void *)addr;
|
||||||
res.maphandle = -1;
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*-*- 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 2020 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/assert.h"
|
|
||||||
#include "libc/calls/internal.h"
|
|
||||||
#include "libc/calls/state.internal.h"
|
|
||||||
#include "libc/errno.h"
|
|
||||||
#include "libc/intrin/directmap.h"
|
|
||||||
#include "libc/nt/enum/filemapflags.h"
|
|
||||||
#include "libc/nt/enum/pageflags.h"
|
|
||||||
#include "libc/nt/errors.h"
|
|
||||||
#include "libc/nt/memory.h"
|
|
||||||
#include "libc/nt/runtime.h"
|
|
||||||
#include "libc/nt/struct/processmemorycounters.h"
|
|
||||||
#include "libc/nt/struct/securityattributes.h"
|
|
||||||
#include "libc/sysv/consts/map.h"
|
|
||||||
#include "libc/sysv/consts/o.h"
|
|
||||||
#include "libc/sysv/consts/prot.h"
|
|
||||||
|
|
||||||
textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot,
|
|
||||||
int flags, int fd, int64_t off) {
|
|
||||||
|
|
||||||
int64_t handle;
|
|
||||||
if (flags & MAP_ANONYMOUS) {
|
|
||||||
handle = kNtInvalidHandleValue;
|
|
||||||
} else {
|
|
||||||
handle = g_fds.p[fd].handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark map handle as inheritable if fork might need it
|
|
||||||
const struct NtSecurityAttributes *mapsec;
|
|
||||||
if ((flags & MAP_TYPE) == MAP_SHARED) {
|
|
||||||
mapsec = &kNtIsInheritable;
|
|
||||||
} else {
|
|
||||||
mapsec = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nt will whine under many circumstances if we change the execute bit
|
|
||||||
// later using mprotect(). the workaround is to always request execute
|
|
||||||
// and then virtualprotect() it away until we actually need it. please
|
|
||||||
// note that open-nt.c always requests an kNtGenericExecute accessmask
|
|
||||||
int iscow = false;
|
|
||||||
struct ProtectNt fl;
|
|
||||||
if (handle != -1) {
|
|
||||||
if ((flags & MAP_TYPE) != MAP_SHARED) {
|
|
||||||
// windows has cow pages but they can't propagate across fork()
|
|
||||||
// that means we only get copy-on-write for the root process :(
|
|
||||||
fl = (struct ProtectNt){kNtPageExecuteWritecopy,
|
|
||||||
kNtFileMapCopy | kNtFileMapExecute};
|
|
||||||
iscow = true;
|
|
||||||
} else {
|
|
||||||
if ((g_fds.p[fd].flags & O_ACCMODE) == O_RDONLY) {
|
|
||||||
fl = (struct ProtectNt){kNtPageExecuteRead,
|
|
||||||
kNtFileMapRead | kNtFileMapExecute};
|
|
||||||
} else {
|
|
||||||
fl = (struct ProtectNt){kNtPageExecuteReadwrite,
|
|
||||||
kNtFileMapWrite | kNtFileMapExecute};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unassert(flags & MAP_ANONYMOUS);
|
|
||||||
fl = (struct ProtectNt){kNtPageExecuteReadwrite,
|
|
||||||
kNtFileMapWrite | kNtFileMapExecute};
|
|
||||||
}
|
|
||||||
|
|
||||||
int e = errno;
|
|
||||||
struct DirectMap dm;
|
|
||||||
TryAgain:
|
|
||||||
if ((dm.maphandle = CreateFileMapping(handle, mapsec, fl.flags1,
|
|
||||||
(size + off) >> 32, (size + off), 0))) {
|
|
||||||
if ((dm.addr = MapViewOfFileEx(dm.maphandle, fl.flags2, off >> 32, off,
|
|
||||||
size, addr))) {
|
|
||||||
uint32_t oldprot;
|
|
||||||
if (VirtualProtect(dm.addr, size, __prot2nt(prot, iscow), &oldprot))
|
|
||||||
return dm;
|
|
||||||
UnmapViewOfFile(dm.addr);
|
|
||||||
}
|
|
||||||
CloseHandle(dm.maphandle);
|
|
||||||
} else if (!(prot & PROT_EXEC) && //
|
|
||||||
(fl.flags2 & kNtFileMapExecute) && //
|
|
||||||
GetLastError() == kNtErrorAccessDenied) {
|
|
||||||
// your file needs to have been O_CREAT'd with exec `mode` bits in
|
|
||||||
// order to be mapped with executable permission. we always try to
|
|
||||||
// get execute permission if the kernel will give it to us because
|
|
||||||
// win32 would otherwise forbid mprotect() from elevating later on
|
|
||||||
fl.flags2 &= ~kNtFileMapExecute;
|
|
||||||
switch (fl.flags1) {
|
|
||||||
case kNtPageExecuteWritecopy:
|
|
||||||
fl.flags1 = kNtPageWritecopy;
|
|
||||||
break;
|
|
||||||
case kNtPageExecuteReadwrite:
|
|
||||||
fl.flags1 = kNtPageReadwrite;
|
|
||||||
break;
|
|
||||||
case kNtPageExecuteRead:
|
|
||||||
fl.flags1 = kNtPageReadonly;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
errno = e;
|
|
||||||
goto TryAgain;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm.maphandle = kNtInvalidHandleValue;
|
|
||||||
dm.addr = (void *)(intptr_t)-1;
|
|
||||||
return dm;
|
|
||||||
}
|
|
|
@ -2,19 +2,7 @@
|
||||||
#define COSMOPOLITAN_LIBC_INTRIN_DIRECTMAP_H_
|
#define COSMOPOLITAN_LIBC_INTRIN_DIRECTMAP_H_
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
struct ProtectNt {
|
void *sys_mmap_metal(void *, size_t, int, int, int, int64_t) libcesque;
|
||||||
uint32_t flags1;
|
|
||||||
uint32_t flags2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DirectMap {
|
|
||||||
void *addr;
|
|
||||||
int64_t maphandle;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DirectMap sys_mmap(void *, size_t, int, int, int, int64_t);
|
|
||||||
struct DirectMap sys_mmap_nt(void *, size_t, int, int, int, int64_t);
|
|
||||||
struct DirectMap sys_mmap_metal(void *, size_t, int, int, int, int64_t);
|
|
||||||
int sys_munmap_metal(void *, size_t) libcesque;
|
int sys_munmap_metal(void *, size_t) libcesque;
|
||||||
int __prot2nt(int, int) libcesque;
|
int __prot2nt(int, int) libcesque;
|
||||||
|
|
||||||
|
|
34
libc/intrin/dlopen.c
Normal file
34
libc/intrin/dlopen.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*-*- 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 2024 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/thread/posixthread.internal.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
static pthread_mutex_t __dlopen_lock_obj = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void __dlopen_lock(void) {
|
||||||
|
_pthread_mutex_lock(&__dlopen_lock_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __dlopen_unlock(void) {
|
||||||
|
_pthread_mutex_unlock(&__dlopen_lock_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __dlopen_wipe(void) {
|
||||||
|
_pthread_mutex_wipe_np(&__dlopen_lock_obj);
|
||||||
|
}
|
|
@ -44,15 +44,13 @@
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
#include "libc/thread/tls.h"
|
||||||
#define OPEN_MAX 16
|
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
__static_yoink("_init_fds");
|
__static_yoink("_init_fds");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct Fds g_fds;
|
struct Fds g_fds;
|
||||||
static struct Fd g_fds_static[OPEN_MAX];
|
|
||||||
|
|
||||||
static bool TokAtoi(const char **str, long *res) {
|
static bool TokAtoi(const char **str, long *res) {
|
||||||
int c, d;
|
int c, d;
|
||||||
|
@ -86,19 +84,14 @@ static textwindows void SetupWinStd(struct Fds *fds, int i, uint32_t x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
textstartup void __init_fds(int argc, char **argv, char **envp) {
|
textstartup void __init_fds(int argc, char **argv, char **envp) {
|
||||||
|
|
||||||
struct Fds *fds;
|
struct Fds *fds;
|
||||||
fds = &g_fds;
|
fds = &g_fds;
|
||||||
fds->n = 4;
|
fds->n = 4;
|
||||||
atomic_store_explicit(&fds->f, 3, memory_order_relaxed);
|
atomic_store_explicit(&fds->f, 3, memory_order_relaxed);
|
||||||
if (_weaken(_extend)) {
|
fds->p = fds->e = (void *)kMemtrackFdsStart;
|
||||||
fds->p = fds->e = (void *)kMemtrackFdsStart;
|
fds->e = _extend(fds->p, fds->n * sizeof(*fds->p), fds->e, MAP_PRIVATE,
|
||||||
fds->e =
|
kMemtrackFdsStart + kMemtrackFdsSize);
|
||||||
_weaken(_extend)(fds->p, fds->n * sizeof(*fds->p), fds->e, MAP_PRIVATE,
|
|
||||||
kMemtrackFdsStart + kMemtrackFdsSize);
|
|
||||||
} else {
|
|
||||||
fds->p = g_fds_static;
|
|
||||||
fds->e = g_fds_static + OPEN_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// inherit standard i/o file descriptors
|
// inherit standard i/o file descriptors
|
||||||
if (IsMetal()) {
|
if (IsMetal()) {
|
||||||
|
@ -152,8 +145,7 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
|
||||||
break;
|
break;
|
||||||
if (!TokAtoi(&fdspec, &protocol))
|
if (!TokAtoi(&fdspec, &protocol))
|
||||||
break;
|
break;
|
||||||
if (_weaken(__ensurefds_unlocked))
|
__ensurefds_unlocked(fd);
|
||||||
_weaken(__ensurefds_unlocked)(fd);
|
|
||||||
struct Fd *f = fds->p + fd;
|
struct Fd *f = fds->p + fd;
|
||||||
if (f->handle && f->handle != -1 && f->handle != handle) {
|
if (f->handle && f->handle != -1 && f->handle != handle) {
|
||||||
CloseHandle(f->handle);
|
CloseHandle(f->handle);
|
||||||
|
@ -188,7 +180,9 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
|
||||||
map->prot = PROT_READ | PROT_WRITE;
|
map->prot = PROT_READ | PROT_WRITE;
|
||||||
map->flags = MAP_SHARED | MAP_ANONYMOUS;
|
map->flags = MAP_SHARED | MAP_ANONYMOUS;
|
||||||
map->hand = shand;
|
map->hand = shand;
|
||||||
|
__maps_lock();
|
||||||
__maps_insert(map);
|
__maps_insert(map);
|
||||||
|
__maps_unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,13 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
void __fds_lock(void) {
|
void __fds_lock(void) {
|
||||||
pthread_mutex_lock(&__fds_lock_obj);
|
_pthread_mutex_lock(&__fds_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __fds_unlock(void) {
|
void __fds_unlock(void) {
|
||||||
pthread_mutex_unlock(&__fds_lock_obj);
|
_pthread_mutex_unlock(&__fds_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,9 @@
|
||||||
// @see setcontext()
|
// @see setcontext()
|
||||||
.ftrace1
|
.ftrace1
|
||||||
getcontext:
|
getcontext:
|
||||||
|
beg
|
||||||
.ftrace2
|
.ftrace2
|
||||||
#include "libc/calls/getcontext.inc"
|
#include "libc/intrin/getcontext.inc"
|
||||||
jmp __getcontextsig
|
jmp __getcontextsig
|
||||||
|
end
|
||||||
.endfn getcontext,globl
|
.endfn getcontext,globl
|
|
@ -34,6 +34,7 @@
|
||||||
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
|
|
@ -17,16 +17,13 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/struct/rlimit.h"
|
#include "libc/calls/struct/rlimit.h"
|
||||||
#include "libc/calls/struct/rlimit.internal.h"
|
|
||||||
#include "libc/dce.h"
|
|
||||||
#include "libc/intrin/getauxval.h"
|
#include "libc/intrin/getauxval.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
|
||||||
#include "libc/intrin/maps.h"
|
#include "libc/intrin/maps.h"
|
||||||
|
#include "libc/intrin/rlimit.h"
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/stdio/sysparam.h"
|
||||||
#include "libc/sysv/consts/auxv.h"
|
#include "libc/sysv/consts/auxv.h"
|
||||||
#include "libc/sysv/consts/rlim.h"
|
|
||||||
#include "libc/sysv/consts/rlimit.h"
|
|
||||||
|
|
||||||
// Hack for guessing boundaries of _start()'s stack
|
// Hack for guessing boundaries of _start()'s stack
|
||||||
//
|
//
|
||||||
|
@ -91,12 +88,9 @@ static uintptr_t __get_main_top(int pagesz) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t __get_stack_size(int pagesz, uintptr_t start, uintptr_t top) {
|
static size_t __get_stack_size(int pagesz, uintptr_t start, uintptr_t top) {
|
||||||
size_t size, max = 8 * 1024 * 1024;
|
size_t stacksz = __rlimit_stack_get().rlim_cur;
|
||||||
struct rlimit rlim = {RLIM_INFINITY};
|
stacksz = MIN(stacksz, 1024ul * 1024 * 1024 * 1024);
|
||||||
sys_getrlimit(RLIMIT_STACK, &rlim);
|
return MAX(ROUNDDOWN(stacksz, pagesz), ROUNDUP(top - start, pagesz));
|
||||||
if ((size = rlim.rlim_cur) > max)
|
|
||||||
size = max;
|
|
||||||
return MAX(ROUNDUP(size, pagesz), ROUNDUP(top - start, pagesz));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,18 +16,47 @@
|
||||||
│ 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/struct/siginfo.h"
|
||||||
|
#include "libc/calls/ucontext.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/getauxval.h"
|
#include "libc/intrin/getauxval.h"
|
||||||
#include "libc/macros.h"
|
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/consts/auxv.h"
|
#include "libc/sysv/consts/auxv.h"
|
||||||
#include "libc/sysv/consts/ss.h"
|
#include "libc/sysv/consts/ss.h"
|
||||||
|
|
||||||
long __get_minsigstksz(void) {
|
long __get_minsigstksz(void) {
|
||||||
struct AuxiliaryValue x;
|
struct AuxiliaryValue av;
|
||||||
x = __getauxval(AT_MINSIGSTKSZ);
|
av = __getauxval(AT_MINSIGSTKSZ);
|
||||||
if (x.isfound) {
|
if (av.isfound) {
|
||||||
return MAX(_MINSIGSTKSZ, x.value);
|
long res = av.value;
|
||||||
|
if (!IsLinux())
|
||||||
|
res += sizeof(struct ucontext) + sizeof(struct siginfo) + 128;
|
||||||
|
if (res < _MINSIGSTKSZ)
|
||||||
|
res = _MINSIGSTKSZ;
|
||||||
|
return res;
|
||||||
} else {
|
} else {
|
||||||
|
// _MINSIGSTKSZ takes these things into consideration:
|
||||||
|
//
|
||||||
|
// 1. The platform definition of MINSIGSTKSZ. This will probably be
|
||||||
|
// enforced by the kernel when calling sys_sigaltstack(). On ARM
|
||||||
|
// platforms this might be several kilobytes larger than x86. On
|
||||||
|
// Linux they really want you to use AT_MINSIGSTKSZ instead. The
|
||||||
|
// kernel should ideally set this to be the number of bytes that
|
||||||
|
// get subtracted from the stack pointer when delivering signals
|
||||||
|
// meaning that if you use this for a stack size your handler is
|
||||||
|
// called successfully but if it uses the stack then it'll crash
|
||||||
|
//
|
||||||
|
// 2. Cosmo sigenter overhead. On non-Linux OSes the kernel calls a
|
||||||
|
// trampoline in the libc runtime, which translates the platform
|
||||||
|
// specific signal frame to the Linux memory layout. It means we
|
||||||
|
// need to push ~1024 extra bytes on the stack to call a handler
|
||||||
|
//
|
||||||
|
// 3. Sanity testing. Assume we use sysconf(_SC_MINSIGSTKSZ) + 2048
|
||||||
|
// as our stack size (see stackoverflow1_test.c). Then we should
|
||||||
|
// have enough room to use kprintf() from our signal handler. If
|
||||||
|
// that isn't the case, then this should be increased a bit more
|
||||||
|
// noting that if 1024 is used then kprintf should print refusal
|
||||||
|
//
|
||||||
return _MINSIGSTKSZ;
|
return _MINSIGSTKSZ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,11 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "ape/sections.internal.h"
|
#include "ape/sections.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/runtime/stack.h"
|
#include "libc/runtime/stack.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
#include "libc/thread/tls2.internal.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes safer buffer size for alloca().
|
* Computes safer buffer size for alloca().
|
||||||
|
@ -32,18 +31,19 @@
|
||||||
* @return number of bytes to use for your buffer, or negative if the
|
* @return number of bytes to use for your buffer, or negative if the
|
||||||
* allocation would likely cause a stack overflow
|
* allocation would likely cause a stack overflow
|
||||||
*/
|
*/
|
||||||
privileged long __get_safe_size(long want, long extraspace) {
|
privileged optimizesize long __get_safe_size(long want, long extraspace) {
|
||||||
if (!__tls_enabled)
|
if (!__tls_enabled)
|
||||||
return want;
|
return want;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
struct CosmoTib *tib = __get_tls_privileged();
|
struct CosmoTib *tib = __get_tls_privileged();
|
||||||
long bottom, sp = GetStackPointer();
|
long bottom, sp = GetStackPointer();
|
||||||
if ((char *)sp >= tib->tib_sigstack_addr &&
|
if (sp >= (long)tib->tib_sigstack_addr &&
|
||||||
(char *)sp <= tib->tib_sigstack_addr + tib->tib_sigstack_size) {
|
sp < (long)tib->tib_sigstack_addr + tib->tib_sigstack_size) {
|
||||||
bottom = (long)tib->tib_sigstack_addr;
|
bottom = (long)tib->tib_sigstack_addr;
|
||||||
} else if ((pt = (struct PosixThread *)tib->tib_pthread) &&
|
} else if ((pt = (struct PosixThread *)tib->tib_pthread) &&
|
||||||
pt->pt_attr.__stacksize) {
|
sp >= (long)pt->pt_attr.__stackaddr &&
|
||||||
bottom = (long)pt->pt_attr.__stackaddr + pt->pt_attr.__guardsize;
|
sp < (long)pt->pt_attr.__stackaddr + pt->pt_attr.__stacksize) {
|
||||||
|
bottom = (long)pt->pt_attr.__stackaddr;
|
||||||
} else {
|
} else {
|
||||||
return want;
|
return want;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
int gettid(void) {
|
int gettid(void) {
|
||||||
int tid;
|
int tid;
|
||||||
if (VERY_LIKELY(__tls_enabled && !__vforked)) {
|
if (VERY_LIKELY(__tls_enabled && !__vforked)) {
|
||||||
tid = atomic_load_explicit(&__get_tls()->tib_tid, memory_order_acquire);
|
tid = atomic_load_explicit(&__get_tls()->tib_ptid, memory_order_relaxed);
|
||||||
if (VERY_LIKELY(tid > 0))
|
if (VERY_LIKELY(tid > 0))
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
|
|
43
libc/intrin/itimer.c
Normal file
43
libc/intrin/itimer.c
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*-*- 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 2024 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/thread/itimer.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
|
||||||
|
struct IntervalTimer __itimer = {
|
||||||
|
.lock = PTHREAD_MUTEX_INITIALIZER,
|
||||||
|
.cond = PTHREAD_COND_INITIALIZER,
|
||||||
|
};
|
||||||
|
|
||||||
|
textwindows void __itimer_lock(void) {
|
||||||
|
_pthread_mutex_lock(&__itimer.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
textwindows void __itimer_unlock(void) {
|
||||||
|
_pthread_mutex_unlock(&__itimer.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
textwindows void __itimer_wipe_and_reset(void) {
|
||||||
|
// timers aren't inherited by forked subprocesses
|
||||||
|
bzero(&__itimer.it, sizeof(__itimer.it));
|
||||||
|
_pthread_mutex_wipe_np(&__itimer.lock);
|
||||||
|
bzero(&__itimer.cond, sizeof(__itimer.cond));
|
||||||
|
__itimer.thread = 0;
|
||||||
|
__itimer.once = 0;
|
||||||
|
}
|
38
libc/intrin/kisdangerous.c
Normal file
38
libc/intrin/kisdangerous.c
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*-*- 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 2024 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/intrin/kprintf.h"
|
||||||
|
#include "libc/intrin/maps.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
|
||||||
|
privileged optimizesize bool32 kisdangerous(const void *addr) {
|
||||||
|
bool32 res = true;
|
||||||
|
__maps_lock();
|
||||||
|
if (__maps.maps) {
|
||||||
|
struct Map *map;
|
||||||
|
if ((map = __maps_floor(addr)))
|
||||||
|
if ((const char *)addr >= map->addr &&
|
||||||
|
(const char *)addr <
|
||||||
|
map->addr + ((map->size + __pagesize - 1) & -__pagesize))
|
||||||
|
res = false;
|
||||||
|
} else {
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
__maps_unlock();
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -65,10 +65,11 @@
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
#include "libc/thread/tls2.internal.h"
|
|
||||||
#include "libc/vga/vga.internal.h"
|
#include "libc/vga/vga.internal.h"
|
||||||
#include "libc/wctype.h"
|
#include "libc/wctype.h"
|
||||||
|
|
||||||
|
#define ABI privileged optimizesize
|
||||||
|
|
||||||
#define STACK_ERROR "kprintf error: stack is about to overflow\n"
|
#define STACK_ERROR "kprintf error: stack is about to overflow\n"
|
||||||
|
|
||||||
#define KGETINT(x, va, t, s) \
|
#define KGETINT(x, va, t, s) \
|
||||||
|
@ -159,23 +160,7 @@ __funline bool kischarmisaligned(const char *p, signed char t) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged bool32 kisdangerous(const void *addr) {
|
ABI static void klogclose(long fd) {
|
||||||
bool32 res = true;
|
|
||||||
__maps_lock();
|
|
||||||
if (__maps.maps) {
|
|
||||||
struct Map *map;
|
|
||||||
if ((map = __maps_floor(addr)))
|
|
||||||
if ((const char *)addr >= map->addr &&
|
|
||||||
(const char *)addr < map->addr + map->size)
|
|
||||||
res = false;
|
|
||||||
} else {
|
|
||||||
res = false;
|
|
||||||
}
|
|
||||||
__maps_unlock();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
privileged static void klogclose(long fd) {
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
long ax = __NR_close;
|
long ax = __NR_close;
|
||||||
asm volatile("syscall"
|
asm volatile("syscall"
|
||||||
|
@ -192,7 +177,7 @@ privileged static void klogclose(long fd) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged static long klogfcntl(long fd, long cmd, long arg) {
|
ABI static long klogfcntl(long fd, long cmd, long arg) {
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
char cf;
|
char cf;
|
||||||
long ax = __NR_fcntl;
|
long ax = __NR_fcntl;
|
||||||
|
@ -224,7 +209,7 @@ privileged static long klogfcntl(long fd, long cmd, long arg) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged static long klogopen(const char *path) {
|
ABI static long klogopen(const char *path) {
|
||||||
long dirfd = AT_FDCWD;
|
long dirfd = AT_FDCWD;
|
||||||
long flags = O_WRONLY | O_CREAT | O_APPEND;
|
long flags = O_WRONLY | O_CREAT | O_APPEND;
|
||||||
long mode = 0600;
|
long mode = 0600;
|
||||||
|
@ -263,7 +248,7 @@ privileged static long klogopen(const char *path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns log handle or -1 if logging shouldn't happen
|
// returns log handle or -1 if logging shouldn't happen
|
||||||
privileged long kloghandle(void) {
|
ABI long kloghandle(void) {
|
||||||
// kprintf() needs to own a file descriptor in case apps closes stderr
|
// kprintf() needs to own a file descriptor in case apps closes stderr
|
||||||
// our close() and dup() implementations will trigger this initializer
|
// our close() and dup() implementations will trigger this initializer
|
||||||
// to minimize a chance that the user accidentally closes their logger
|
// to minimize a chance that the user accidentally closes their logger
|
||||||
|
@ -342,7 +327,7 @@ privileged long kloghandle(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
privileged void _klog_serial(const char *b, size_t n) {
|
ABI void _klog_serial(const char *b, size_t n) {
|
||||||
size_t i;
|
size_t i;
|
||||||
uint16_t dx;
|
uint16_t dx;
|
||||||
unsigned char al;
|
unsigned char al;
|
||||||
|
@ -362,14 +347,13 @@ privileged void _klog_serial(const char *b, size_t n) {
|
||||||
}
|
}
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
||||||
privileged void klog(const char *b, size_t n) {
|
ABI void klog(const char *b, size_t n) {
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
long h;
|
long h;
|
||||||
uint32_t wrote;
|
uint32_t wrote;
|
||||||
long rax, rdi, rsi, rdx;
|
long rax, rdi, rsi, rdx;
|
||||||
if ((h = kloghandle()) == -1) {
|
if ((h = kloghandle()) == -1)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
if (IsWindows()) {
|
if (IsWindows()) {
|
||||||
bool32 ok;
|
bool32 ok;
|
||||||
intptr_t ev;
|
intptr_t ev;
|
||||||
|
@ -420,14 +404,14 @@ privileged void klog(const char *b, size_t n) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
ABI static size_t kformat(char *b, size_t n, const char *fmt, va_list va) {
|
||||||
va_list va) {
|
|
||||||
int si;
|
int si;
|
||||||
wint_t t, u;
|
wint_t t, u;
|
||||||
|
char *cxxbuf;
|
||||||
const char *abet;
|
const char *abet;
|
||||||
signed char type;
|
signed char type;
|
||||||
const char *s, *f;
|
const char *s, *f;
|
||||||
char cxxbuf[3000];
|
int cxxbufsize = 0;
|
||||||
struct CosmoTib *tib;
|
struct CosmoTib *tib;
|
||||||
unsigned long long x;
|
unsigned long long x;
|
||||||
unsigned i, j, m, rem, sign, hash, cols, prec;
|
unsigned i, j, m, rem, sign, hash, cols, prec;
|
||||||
|
@ -577,7 +561,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
||||||
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
||||||
if (!(tib && (tib->tib_flags & TIB_FLAG_VFORKED))) {
|
if (!(tib && (tib->tib_flags & TIB_FLAG_VFORKED))) {
|
||||||
if (tib) {
|
if (tib) {
|
||||||
x = atomic_load_explicit(&tib->tib_tid, memory_order_relaxed);
|
x = atomic_load_explicit(&tib->tib_ptid, memory_order_relaxed);
|
||||||
} else {
|
} else {
|
||||||
x = __pid;
|
x = __pid;
|
||||||
}
|
}
|
||||||
|
@ -771,13 +755,25 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
||||||
x = va_arg(va, intptr_t);
|
x = va_arg(va, intptr_t);
|
||||||
if (_weaken(__symtab) && *_weaken(__symtab) &&
|
if (_weaken(__symtab) && *_weaken(__symtab) &&
|
||||||
(idx = _weaken(__get_symbol)(0, x)) != -1) {
|
(idx = _weaken(__get_symbol)(0, x)) != -1) {
|
||||||
/* if (p + 1 <= e) */
|
|
||||||
/* *p++ = '&'; */
|
|
||||||
s = (*_weaken(__symtab))->name_base +
|
s = (*_weaken(__symtab))->name_base +
|
||||||
(*_weaken(__symtab))->names[idx];
|
(*_weaken(__symtab))->names[idx];
|
||||||
if (_weaken(__is_mangled) && _weaken(__is_mangled)(s) &&
|
#pragma GCC push_options
|
||||||
_weaken(__demangle)(cxxbuf, s, sizeof(cxxbuf)) != -1)
|
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||||
s = cxxbuf;
|
// decipher c++ symbols if there's enough stack memory
|
||||||
|
// stack size requirement assumes max_depth's still 20
|
||||||
|
if (_weaken(__demangle) && //
|
||||||
|
_weaken(__is_mangled) && //
|
||||||
|
_weaken(__is_mangled)(s)) {
|
||||||
|
if (!cxxbufsize)
|
||||||
|
if ((cxxbufsize = __get_safe_size(8192, 8192)) >= 512) {
|
||||||
|
cxxbuf = alloca(cxxbufsize);
|
||||||
|
CheckLargeStackAllocation(cxxbuf, sizeof(cxxbufsize));
|
||||||
|
}
|
||||||
|
if (cxxbufsize >= 512)
|
||||||
|
if (_weaken(__demangle)(cxxbuf, s, cxxbufsize) != -1)
|
||||||
|
s = cxxbuf;
|
||||||
|
}
|
||||||
|
#pragma GCC pop_options
|
||||||
goto FormatString;
|
goto FormatString;
|
||||||
}
|
}
|
||||||
base = 4;
|
base = 4;
|
||||||
|
@ -1033,7 +1029,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
* @vforksafe
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
privileged size_t ksnprintf(char *b, size_t n, const char *fmt, ...) {
|
ABI size_t ksnprintf(char *b, size_t n, const char *fmt, ...) {
|
||||||
size_t m;
|
size_t m;
|
||||||
va_list v;
|
va_list v;
|
||||||
va_start(v, fmt);
|
va_start(v, fmt);
|
||||||
|
@ -1052,7 +1048,7 @@ privileged size_t ksnprintf(char *b, size_t n, const char *fmt, ...) {
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
* @vforksafe
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) {
|
ABI size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) {
|
||||||
return kformat(b, n, fmt, v);
|
return kformat(b, n, fmt, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1063,10 +1059,10 @@ privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) {
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
* @vforksafe
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
privileged void kvprintf(const char *fmt, va_list v) {
|
ABI void kvprintf(const char *fmt, va_list v) {
|
||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||||
long size = __get_safe_size(8000, 8000);
|
long size = __get_safe_size(8192, 2048);
|
||||||
if (size < 80) {
|
if (size < 80) {
|
||||||
klog(STACK_ERROR, sizeof(STACK_ERROR) - 1);
|
klog(STACK_ERROR, sizeof(STACK_ERROR) - 1);
|
||||||
return;
|
return;
|
||||||
|
@ -1149,7 +1145,7 @@ privileged void kvprintf(const char *fmt, va_list v) {
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
* @vforksafe
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
privileged void kprintf(const char *fmt, ...) {
|
ABI void kprintf(const char *fmt, ...) {
|
||||||
// system call support runtime depends on this function
|
// system call support runtime depends on this function
|
||||||
// function tracing runtime depends on this function
|
// function tracing runtime depends on this function
|
||||||
// asan runtime depends on this function
|
// asan runtime depends on this function
|
||||||
|
|
34
libc/intrin/localtime_lock.c
Normal file
34
libc/intrin/localtime_lock.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*-*- 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 2024 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/thread/posixthread.internal.h"
|
||||||
|
#include "third_party/tz/lock.h"
|
||||||
|
|
||||||
|
static pthread_mutex_t __localtime_lock_obj = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void __localtime_lock(void) {
|
||||||
|
_pthread_mutex_lock(&__localtime_lock_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __localtime_unlock(void) {
|
||||||
|
_pthread_mutex_unlock(&__localtime_lock_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __localtime_wipe(void) {
|
||||||
|
_pthread_mutex_wipe_np(&__localtime_lock_obj);
|
||||||
|
}
|
50
libc/intrin/lockless.h
Normal file
50
libc/intrin/lockless.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_INTRIN_LOCKLESS_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_INTRIN_LOCKLESS_H_
|
||||||
|
#include "libc/atomic.h"
|
||||||
|
#include "libc/intrin/atomic.h"
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
// lockless memory transactions
|
||||||
|
//
|
||||||
|
// - one writer
|
||||||
|
// - many readers
|
||||||
|
// - generation is monotonic
|
||||||
|
// - even numbers mean memory is ready
|
||||||
|
// - odd numbers mean memory is actively being changed
|
||||||
|
// - always use acquire semantics inside your read transaction
|
||||||
|
//
|
||||||
|
// let's say you want to be able to atomically read and write to 128-bit
|
||||||
|
// values, but you've only got a 64-bit system. if you expect that it'll
|
||||||
|
// frequently written, then you should use a mutex. but if you expect it
|
||||||
|
// to be frequently read and rarely written, then it's possible to do it
|
||||||
|
// without a mutex; in fact you don't even need the x86 lock instruction
|
||||||
|
// prefix; all that is required is a series of carefully ordered mov ops
|
||||||
|
// which are designed to exploit the strong ordering of the architecture
|
||||||
|
|
||||||
|
static inline unsigned lockless_write_begin(atomic_uint* genptr) {
|
||||||
|
unsigned gen = atomic_load_explicit(genptr, memory_order_acquire);
|
||||||
|
atomic_store_explicit(genptr, gen + 1, memory_order_release);
|
||||||
|
return gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lockless_write_end(atomic_uint* genptr, unsigned gen) {
|
||||||
|
atomic_store_explicit(genptr, gen + 2, memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned lockless_read_begin(atomic_uint* genptr) {
|
||||||
|
return atomic_load_explicit(genptr, memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool lockless_read_end(atomic_uint* genptr, unsigned* want) {
|
||||||
|
unsigned gen1 = *want;
|
||||||
|
unsigned gen2 = atomic_load_explicit(genptr, memory_order_acquire);
|
||||||
|
unsigned is_being_actively_changed = gen1 & 1;
|
||||||
|
unsigned we_lost_race_with_writers = gen1 ^ gen2;
|
||||||
|
if (!(is_being_actively_changed | we_lost_race_with_writers))
|
||||||
|
return true;
|
||||||
|
*want = gen2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_INTRIN_LOCKLESS_H_ */
|
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