Compare commits

...
49 Commits
Author SHA1 Message Date
Justine Tunney 746660066f Release Cosmopolitan v3.9.7 2024-11-22 21:38:09 -08:00
Justine Tunney fd15b2d7a3 Ensure ^C gets printed to Windows console 2024-11-22 14:56:53 -08:00
Justine Tunney e228aa3e14 Save rax register in getcontext 2024-11-22 13:32:52 -08:00
Justine Tunney 9ddbfd921e Introduce cosmo_futex_wait and cosmo_futex_wake
Cosmopolitan Futexes are now exposed as a public API.
2024-11-22 11:25:15 -08:00
Justine Tunney 729f7045e3 Cleanup terminal on ^C in asteroids game 2024-11-22 08:52:49 -08:00
Justine Tunney e47d67ba9b Add asteroids game
Source code is the same as upstream, aside from a header added.
2024-11-22 08:46:33 -08:00
Justine Tunney 2477677c85 Delete superfluous definition 2024-11-22 08:27:42 -08:00
Justine Tunney abdf6c9c26 Sync with jtckdint 2024-11-20 15:56:56 -08:00
Justine Tunney 5c3f854acb Fix strcasestr()
Fixes #1323
2024-11-20 14:07:17 -08:00
BONNAURE Olivier ad0a7c67c4 [redbean] Add details to OnError Hook (#1324)
The details of the error was missing, this PR add details to the OnError
hook so we can now get why the error occurs
2024-11-20 13:55:45 -08:00
Justine Tunney 1312f60245 Strongly link tr and sed into system() and popen() 2024-11-15 21:23:49 -08:00
Justine Tunney cafdb456ed Strongly link glob() into system() and popen() 2024-11-15 20:37:34 -08:00
Justine Tunney 4e9566cd33 Invent new cosmo_args() api
This function offers a more powerful replacement for LoadZipArgs() which
is now deprecated. By writing your C programs as follows:

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

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

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

    # prevent settings below from being changed
    ...

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

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

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

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

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

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

This commit adds this conversion specifier alongside a few tests for it.
2024-09-26 04:27:45 -07:00
Justine Tunney 518eabadf5 Further optimize poll() on Windows 2024-09-22 22:28:59 -07:00
Justine Tunney 556a294363 Improve Windows mode bits
We were too zealous about security before by only setting the owner bits
and that would cause issues for projects like redbean that check "other"
bits to determine if it's safe to serve a file. Since that doesn't exist
on Windows, it's better to have things work than not work. So what we'll
do instead is return modes like 0664 for files and 0775 for directories.
2024-09-22 16:51:57 -07:00
Justine Tunney 80804ccfff Upgrade to cosmocc v3.9.2 2024-09-22 03:57:35 -07:00
288 changed files with 4378 additions and 1868 deletions

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
{
"C_Cpp.default.compilerPath": ".cosmocc/3.8.0/bin/aarch64-linux-cosmo-c++",
"C_Cpp.default.compilerPath": ".cosmocc/3.9.2/bin/aarch64-linux-cosmo-c++",
"C_Cpp.default.compilerArgs": [
"-nostdinc",
"-nostdlib",
+59 -57
View File
@@ -147,10 +147,10 @@ export MODE
export SOURCE_DATE_EPOCH
export TMPDIR
COSMOCC = .cosmocc/3.8.0
COSMOCC = .cosmocc/3.9.2
BOOTSTRAP = $(COSMOCC)/bin
TOOLCHAIN = $(COSMOCC)/bin/$(ARCH)-linux-cosmo-
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.8.0 813c6b2f95062d2e0a845307a79505424cb98cb038e8013334f8a22e3b92a474)
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.9.2 f4ff13af65fcd309f3f1cfd04275996fb7f72a4897726628a8c9cf732e850193)
IGNORE := $(shell $(MKDIR) $(TMPDIR))
@@ -274,6 +274,10 @@ include libc/BUILD.mk #─┘
include libc/sock/BUILD.mk #─┐
include net/http/BUILD.mk # ├──ONLINE RUNTIME
include third_party/musl/BUILD.mk # │ You can communicate with the network
include third_party/regex/BUILD.mk # │
include third_party/tr/BUILD.mk # │
include third_party/sed/BUILD.mk # │
include libc/system/BUILD.mk # │
include libc/x/BUILD.mk # │
include dsp/scale/BUILD.mk # │
include dsp/mpeg/BUILD.mk # │
@@ -293,8 +297,7 @@ include third_party/libcxx/BUILD.mk # │
include third_party/openmp/BUILD.mk # │
include third_party/pcre/BUILD.mk # │
include third_party/less/BUILD.mk # │
include net/https/BUILD.mk # │
include third_party/regex/BUILD.mk #─┘
include net/https/BUILD.mk #─┘
include third_party/tidy/BUILD.mk
include third_party/BUILD.mk
include third_party/nsync/testing/BUILD.mk
@@ -313,8 +316,6 @@ include third_party/double-conversion/test/BUILD.mk
include third_party/lua/BUILD.mk
include third_party/tree/BUILD.mk
include third_party/zstd/BUILD.mk
include third_party/tr/BUILD.mk
include third_party/sed/BUILD.mk
include third_party/awk/BUILD.mk
include third_party/hiredis/BUILD.mk
include third_party/make/BUILD.mk
@@ -367,6 +368,7 @@ include test/libc/fmt/BUILD.mk
include test/libc/time/BUILD.mk
include test/libc/proc/BUILD.mk
include test/libc/stdio/BUILD.mk
include test/libc/system/BUILD.mk
include test/libc/BUILD.mk
include test/net/http/BUILD.mk
include test/net/https/BUILD.mk
@@ -433,64 +435,64 @@ loc: o/$(MODE)/tool/build/summy
find -name \*.h -or -name \*.hpp -or -name \*.c -or -name \*.cc -or -name \*.cpp -or -name \*.S -or -name \*.mk | \
$(XARGS) wc -l | grep total | awk '{print $$1}' | $<
# PLEASE: MAINTAIN TOPOLOGICAL ORDER
# FROM HIGHEST LEVEL TO LOWEST LEVEL
COSMOPOLITAN_OBJECTS = \
COSMOPOLITAN = \
CTL \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_OPENMP \
TOOL_ARGS \
NET_HTTP \
DSP_AUDIO \
LIBC_SOCK \
LIBC_NT_WS2_32 \
LIBC_NT_IPHLPAPI \
LIBC_X \
THIRD_PARTY_GETOPT \
LIBC_CALLS \
LIBC_DLOPEN \
LIBC_ELF \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_LOG \
THIRD_PARTY_TZ \
THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB_GZ \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_BCRYPTPRIMITIVES \
LIBC_NT_COMDLG32 \
LIBC_NT_GDI32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_PDH \
LIBC_NT_POWRPROF \
LIBC_NT_PSAPI \
LIBC_NT_REALTIME \
LIBC_NT_SHELL32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_THREAD \
LIBC_TINYMATH \
LIBC_VGA \
LIBC_X \
NET_HTTP \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
LIBC_STDIO \
THIRD_PARTY_GDTOA \
THIRD_PARTY_REGEX \
LIBC_THREAD \
LIBC_PROC \
THIRD_PARTY_NSYNC_MEM \
LIBC_MEM \
THIRD_PARTY_DLMALLOC \
LIBC_DLOPEN \
LIBC_RUNTIME \
THIRD_PARTY_MUSL \
THIRD_PARTY_NSYNC \
LIBC_ELF \
LIBC_IRQ \
LIBC_CALLS \
LIBC_SYSV_CALLS \
LIBC_VGA \
LIBC_NT_REALTIME \
LIBC_NT_PSAPI \
LIBC_NT_POWRPROF \
LIBC_NT_PDH \
LIBC_NT_GDI32 \
LIBC_NT_COMDLG32 \
LIBC_NT_USER32 \
LIBC_NT_NTDLL \
LIBC_NT_ADVAPI32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_FMT \
THIRD_PARTY_ZLIB \
THIRD_PARTY_NSYNC_MEM \
THIRD_PARTY_OPENMP \
THIRD_PARTY_PUFF \
THIRD_PARTY_COMPILER_RT \
LIBC_TINYMATH \
THIRD_PARTY_REGEX \
THIRD_PARTY_TZ \
THIRD_PARTY_XED \
LIBC_STR \
LIBC_SYSV \
LIBC_INTRIN \
LIBC_NT_BCRYPTPRIMITIVES \
LIBC_NT_KERNEL32 \
LIBC_NEXGEN32E
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
TOOL_ARGS \
COSMOPOLITAN_H_PKGS = \
APE \
@@ -538,7 +540,7 @@ COSMOCC_PKGS = \
THIRD_PARTY_INTEL
o/$(MODE)/cosmopolitan.a: \
$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS))
$(call reverse,$(call uniq,$(foreach x,$(COSMOPOLITAN),$($(x)))))
COSMOCC_HDRS = \
$(wildcard libc/integral/*) \
+12 -12
View File
@@ -7,8 +7,8 @@
a build-once run-anywhere language, like Java, except it doesn't need an
interpreter or virtual machine. Instead, it reconfigures stock GCC and
Clang to output a POSIX-approved polyglot format that runs natively on
Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS with the best
possible performance and the tiniest footprint imaginable.
Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the
best possible performance and the tiniest footprint imaginable.
## Background
@@ -242,16 +242,16 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
## Support Vector
| Platform | Min Version | Circa |
| :--- | ---: | ---: |
| AMD | K8 | 2003 |
| Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 |
| OpenBSD | 7 | 2021 |
| FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 |
| Platform | Min Version | Circa |
| :--- | ---: | ---: |
| AMD | K8 | 2003 |
| Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 |
| OpenBSD | 7.3 or earlier | 2023 |
| FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 |
[1] See our [vista branch](https://github.com/jart/cosmopolitan/tree/vista)
for a community supported version of Cosmopolitan that works on Windows
+8 -8
View File
@@ -6,14 +6,14 @@ if [ -n "$OBJDUMP" ]; then
fi
find_objdump() {
if [ -x .cosmocc/3.8.0/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.8.0/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump"
if [ -x .cosmocc/3.9.2/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.9.2/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump"
else
echo "error: toolchain not found (try running 'cosmocc --update' or 'make' in the cosmo monorepo)" >&2
exit 1
+68
View File
@@ -383,4 +383,72 @@ string::erase(const size_t pos, size_t count) noexcept
return *this;
}
void
string::append(const ctl::string_view& s, size_t pos, size_t count) noexcept
{
append(s.substr(pos, count));
}
size_t
string::find_last_of(char c, size_t pos) const noexcept
{
const char* b = data();
size_t n = size();
if (pos > n)
pos = n;
const char* p = (const char*)memrchr(b, c, pos);
return p ? p - b : npos;
}
size_t
string::find_last_of(ctl::string_view set, size_t pos) const noexcept
{
if (empty() || set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t last = size() - 1;
if (pos > last)
pos = last;
for (;;) {
if (lut[b[pos] & 255])
return pos;
if (!pos)
return npos;
--pos;
}
}
size_t
string::find_first_of(char c, size_t pos) const noexcept
{
size_t n = size();
if (pos >= n)
return npos;
const char* b = data();
const char* p = (const char*)memchr(b + pos, c, n - pos);
return p ? p - b : npos;
}
size_t
string::find_first_of(ctl::string_view set, size_t pos) const noexcept
{
if (set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t n = size();
for (;;) {
if (pos >= n)
return npos;
if (lut[b[pos] & 255])
return pos;
++pos;
}
}
} // namespace ctl
+6 -1
View File
@@ -125,6 +125,7 @@ class string
void append(char, size_t) noexcept;
void append(unsigned long) noexcept;
void append(const void*, size_t) noexcept;
void append(const ctl::string_view&, size_t, size_t = npos) noexcept;
string& insert(size_t, ctl::string_view) noexcept;
string& erase(size_t = 0, size_t = npos) noexcept;
string substr(size_t = 0, size_t = npos) const noexcept;
@@ -136,6 +137,10 @@ class string
bool starts_with(ctl::string_view) const noexcept;
size_t find(char, size_t = 0) const noexcept;
size_t find(ctl::string_view, size_t = 0) const noexcept;
size_t find_first_of(char, size_t = 0) const noexcept;
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
size_t find_last_of(char, size_t = npos) const noexcept;
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
void swap(string& s) noexcept
{
@@ -302,7 +307,7 @@ class string
append(ch);
}
void append(const ctl::string_view s) noexcept
void append(const ctl::string_view& s) noexcept
{
append(s.p, s.n);
}
+62
View File
@@ -108,4 +108,66 @@ string_view::starts_with(const string_view s) const noexcept
return !memcmp(p, s.p, s.n);
}
size_t
string_view::find_last_of(char c, size_t pos) const noexcept
{
const char* b = data();
size_t n = size();
if (pos > n)
pos = n;
const char* p = (const char*)memrchr(b, c, pos);
return p ? p - b : npos;
}
size_t
string_view::find_last_of(ctl::string_view set, size_t pos) const noexcept
{
if (empty() || set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t last = size() - 1;
if (pos > last)
pos = last;
for (;;) {
if (lut[b[pos] & 255])
return pos;
if (!pos)
return npos;
--pos;
}
}
size_t
string_view::find_first_of(char c, size_t pos) const noexcept
{
size_t n = size();
if (pos >= n)
return npos;
const char* b = data();
const char* p = (const char*)memchr(b + pos, c, n - pos);
return p ? p - b : npos;
}
size_t
string_view::find_first_of(ctl::string_view set, size_t pos) const noexcept
{
if (set.empty())
return npos;
bool lut[256] = {};
for (char c : set)
lut[c & 255] = true;
const char* b = data();
size_t n = size();
for (;;) {
if (pos >= n)
return npos;
if (lut[b[pos] & 255])
return pos;
++pos;
}
}
} // namespace ctl
+6 -2
View File
@@ -45,6 +45,10 @@ struct string_view
string_view substr(size_t = 0, size_t = npos) const noexcept;
size_t find(char, size_t = 0) const noexcept;
size_t find(string_view, size_t = 0) const noexcept;
size_t find_first_of(char, size_t = 0) const noexcept;
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
size_t find_last_of(char, size_t = npos) const noexcept;
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
constexpr string_view& operator=(const string_view s) noexcept
{
@@ -109,12 +113,12 @@ struct string_view
return p[n - 1];
}
constexpr const_iterator begin() noexcept
constexpr const_iterator begin() const noexcept
{
return p;
}
constexpr const_iterator end() noexcept
constexpr const_iterator end() const noexcept
{
return p + n;
}
+5 -4
View File
@@ -54,8 +54,8 @@ EXAMPLES_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_MEMORY \
LIBC_NT_KERNEL32 \
LIBC_NT_MEMORY \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
@@ -64,6 +64,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
@@ -81,6 +82,8 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_GETOPT \
THIRD_PARTY_HIREDIS \
THIRD_PARTY_LIBCXX \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_LINENOISE \
THIRD_PARTY_LUA \
THIRD_PARTY_MBEDTLS \
@@ -94,12 +97,10 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_TZ \
THIRD_PARTY_VQSORT \
THIRD_PARTY_XED \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_ZLIB \
TOOL_ARGS \
TOOL_BUILD_LIB \
TOOL_VIZ_LIB
TOOL_VIZ_LIB \
EXAMPLES_DEPS := \
$(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x))))
+353
View File
@@ -0,0 +1,353 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
│ To the extent possible under law, Justine Tunney has waived │
│ all copyright and related or neighboring rights to this file, │
│ as it is written in the following disclaimers: │
│ • http://unlicense.org/ │
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <iconv.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
/**
* @fileoverview program for viewing bbs art files
* @see https://github.com/blocktronics/artpacks
* @see http://www.textfiles.com/art/
*/
#define HELP \
"Usage:\n\
art [-b %d] [-f %s] [-t %s] FILE...\n\
\n\
Flags:\n\
-b NUMBER specifies simulated modem baud rate, which defaults to\n\
2400 since that was the most common modem speed in the\n\
later half of the 1980s during the BBS golden age; you\n\
could also say 300 for the slowest experience possible\n\
or you could say 14.4k to get more of a 90's feel, and\n\
there's also the infamous 56k to bring you back to y2k\n\
-f CHARSET specifies charset of input bytes, where the default is\n\
cp347 which means IBM Code Page 347 a.k.a. DOS\n\
-t CHARSET specifies output charset used by your terminal, and it\n\
defaults to utf8 a.k.a. thompson-pike encoding\n\
\n\
Supported charsets:\n\
utf8, ascii, wchar_t, ucs2be, ucs2le, utf16be, utf16le, ucs4be,\n\
ucs4le, utf16, ucs4, ucs2, eucjp, shiftjis, iso2022jp, gb18030, gbk,\n\
gb2312, big5, euckr, iso88591, latin1, iso88592, iso88593, iso88594,\n\
iso88595, iso88596, iso88597, iso88598, iso88599, iso885910,\n\
iso885911, iso885913, iso885914, iso885915, iso885916, cp1250,\n\
windows1250, cp1251, windows1251, cp1252, windows1252, cp1253,\n\
windows1253, cp1254, windows1254, cp1255, windows1255, cp1256,\n\
windows1256, cp1257, windows1257, cp1258, windows1258, koi8r, koi8u,\n\
cp437, cp850, cp866, ibm1047, cp1047.\n\
\n\
See also:\n\
http://www.textfiles.com/art/\n\
https://github.com/blocktronics/artpacks\n\
\n"
#define INBUFSZ 256
#define OUBUFSZ (INBUFSZ * 6)
#define SLIT(s) ((unsigned)s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0])
// "When new technology comes out, people don't all buy it right away.
// If what they have works, some will wait until it doesn't. A few
// people do get the latest though. In 1984 2400 baud modems became
// available, so some people had them, but many didn't. A BBS list
// from 1986 shows operators were mostly 300 and 1200, but some were
// using 2400. The next 5 years were the hayday of the 2400."
//
// https://forum.vcfed.org/index.php?threads/the-2400-baud-modem.44241/
int baud_rate = 2400; // -b 2400
const char* from_charset = "CP437"; // -f CP437
const char* to_charset = "UTF-8"; // -t UTF-8
volatile sig_atomic_t done;
void on_signal(int sig) {
done = 1;
(void)sig;
}
void print(const char* s) {
(void)!write(STDOUT_FILENO, s, strlen(s));
}
int encode_character(char output[8], const char* codec, wchar_t character) {
size_t inbytesleft = sizeof(wchar_t);
size_t outbytesleft = 7;
char* inbuf = (char*)&character;
char* outbuf = output;
iconv_t cd = iconv_open(codec, "wchar_t");
if (cd == (iconv_t)-1)
return -1;
size_t result = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
iconv_close(cd);
if (result == (size_t)-1)
return -1;
*outbuf = '\0';
return 7 - outbytesleft;
}
void append_replacement_character(char** b) {
int n = encode_character(*b, to_charset, 0xFFFD);
if (n == -1)
n = encode_character(*b, to_charset, '?');
if (n != -1)
*b += n;
}
int compare_time(struct timespec a, struct timespec b) {
int cmp;
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec)))
cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
return cmp;
}
struct timespec add_time(struct timespec x, struct timespec y) {
x.tv_sec += y.tv_sec;
x.tv_nsec += y.tv_nsec;
if (x.tv_nsec >= 1000000000) {
x.tv_nsec -= 1000000000;
x.tv_sec += 1;
}
return x;
}
struct timespec subtract_time(struct timespec a, struct timespec b) {
a.tv_sec -= b.tv_sec;
if (a.tv_nsec < b.tv_nsec) {
a.tv_nsec += 1000000000;
a.tv_sec--;
}
a.tv_nsec -= b.tv_nsec;
return a;
}
struct timespec fromnanos(long long x) {
struct timespec ts;
ts.tv_sec = x / 1000000000;
ts.tv_nsec = x % 1000000000;
return ts;
}
void process_file(const char* path, int fd, iconv_t cd) {
size_t carry = 0;
struct timespec next;
char input_buffer[INBUFSZ];
clock_gettime(CLOCK_MONOTONIC, &next);
for (;;) {
// read from file
ssize_t bytes_read = read(fd, input_buffer + carry, INBUFSZ - carry);
if (!bytes_read)
return;
if (bytes_read == -1) {
perror(path);
done = 1;
return;
}
// modernize character set
char* input_ptr = input_buffer;
size_t input_left = carry + bytes_read;
char output_buffer[OUBUFSZ];
char* output_ptr = output_buffer;
size_t output_left = OUBUFSZ;
size_t ir = iconv(cd, &input_ptr, &input_left, &output_ptr, &output_left);
carry = 0;
if (ir == (size_t)-1) {
if (errno == EINVAL) {
// incomplete multibyte sequence encountered
memmove(input_buffer, input_ptr, input_left);
carry = input_left;
} else if (errno == EILSEQ && input_left) {
// EILSEQ means either
// 1. illegal input sequence encountered
// 2. code not encodable in output codec
//
// so we skip one byte of input, and insert � or ? in the output
// this isn't the most desirable behavior, but it is the best we
// can do, since we don't know specifics about the codecs in use
//
// unlike glibc cosmo's iconv implementation may handle case (2)
// automatically by inserting an asterisk in place of a sequence
++input_ptr;
--input_left;
memmove(input_buffer, input_ptr, input_left);
carry = input_left;
if (output_left >= 8)
append_replacement_character(&output_ptr);
} else {
perror(path);
done = 1;
return;
}
}
// write to terminal
for (char* p = output_buffer; p < output_ptr; p++) {
if (done)
return;
(void)!write(STDOUT_FILENO, p, 1);
// allow arrow keys to change baud rate
int have;
if (ioctl(STDIN_FILENO, FIONREAD, &have)) {
perror("ioctl");
done = 1;
return;
}
if (have > 0) {
char key[4] = {0};
if (read(STDIN_FILENO, key, sizeof(key)) > 0) {
if (SLIT(key) == SLIT("\33[A") || // up
SLIT(key) == SLIT("\33[C")) { // right
baud_rate *= 1.4;
} else if (SLIT(key) == SLIT("\33[B") || // down
SLIT(key) == SLIT("\33[D")) { // left
baud_rate *= 0.6;
}
if (baud_rate < 3)
baud_rate = 3;
if (baud_rate > 1000000000)
baud_rate = 1000000000;
}
}
// insert artificial delay for one byte. we divide by 10 to convert
// bits to bytes, because that is how many bits 8-N-1 encoding used
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
next = add_time(next, fromnanos(1e9 / (baud_rate / 10.)));
if (compare_time(next, now) > 0) {
struct timespec sleep = subtract_time(next, now);
nanosleep(&sleep, 0);
}
}
}
}
int main(int argc, char* argv[]) {
int opt;
while ((opt = getopt(argc, argv, "hb:f:t:")) != -1) {
switch (opt) {
case 'b': {
char* endptr;
double rate = strtod(optarg, &endptr);
if (*endptr == 'k') {
rate *= 1e3;
++endptr;
} else if (*endptr == 'm') {
rate *= 1e6;
++endptr;
}
if (*endptr || baud_rate <= 0) {
fprintf(stderr, "%s: invalid baud rate: %s\n", argv[0], optarg);
exit(1);
}
baud_rate = rate;
break;
}
case 'f':
from_charset = optarg;
break;
case 't':
to_charset = optarg;
break;
case 'h':
fprintf(stderr, HELP, baud_rate, from_charset, to_charset);
exit(0);
default:
fprintf(stderr, "protip: pass the -h flag for help\n");
exit(1);
}
}
if (optind == argc) {
fprintf(stderr, "%s: missing operand\n", argv[0]);
exit(1);
}
// create character transcoder
iconv_t cd = iconv_open(to_charset, from_charset);
if (cd == (iconv_t)-1) {
fprintf(stderr, "error: conversion from %s to %s not supported\n",
from_charset, to_charset);
exit(1);
}
// catch ctrl-c
signal(SIGINT, on_signal);
// don't wait until newline to read() keystrokes
struct termios t;
if (!tcgetattr(STDIN_FILENO, &t)) {
struct termios t2 = t;
t2.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &t2);
}
// Process each file specified on the command line
for (int i = optind; i < argc && !done; i++) {
// open file
int fd = open(argv[i], O_RDONLY);
if (fd == -1) {
perror(argv[i]);
break;
}
// wait between files
if (i > optind)
sleep(1);
print("\33[?25l"); // hide cursor
print("\33[H"); // move cursor to top-left
print("\33[J"); // erase display forward
print("\33[1;24r"); // set scrolling region to first 24 lines
print("\33[?7h"); // enable auto-wrap mode
print("\33[?3l"); // 80 column mode (deccolm) vt100
print("\33[H"); // move cursor to top-left, again
// get busy
process_file(argv[i], fd, cd);
close(fd);
}
// cleanup
iconv_close(cd);
print("\33[s"); // save cursor position
print("\33[?25h"); // show cursor
print("\33[0m"); // reset text attributes (color, bold, etc.)
print("\33[?1049l"); // exit alternate screen mode
print("\33(B"); // exit line drawing and other alt charset modes
print("\33[r"); // reset scrolling region
print("\33[?2004l"); // turn off bracketed paste mode
print("\33[4l"); // exit insert mode
print("\33[?1l\33>"); // exit application keypad mode
print("\33[?7h"); // reset text wrapping mode
print("\33[?12l"); // reset cursor blinking mode
print("\33[?6l"); // reset origin mode
print("\33[20l"); // reset auto newline mode
print("\33[u"); // restore cursor position
// restore terminal
tcsetattr(STDIN_FILENO, TCSANOW, &t);
}
+353
View File
@@ -0,0 +1,353 @@
// -*- mode:c; indent-tabs-mode:nil; c-basic-offset:4 -*-
// vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8
// asteroids by tsotchke
// https://github.com/tsotchke/asteroids
// clang-format off
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <sys/select.h>
#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 24
#define MAX_ASTEROIDS 5
#define MAX_BULLETS 5
typedef struct {
float x, y;
} Vector2;
typedef struct {
Vector2 position;
Vector2 velocity;
float angle;
float radius;
} GameObject;
GameObject spaceship;
GameObject asteroids[MAX_ASTEROIDS];
GameObject bullets[MAX_BULLETS];
int score = 0;
time_t startTime;
int isGameOver = 0;
int shouldExit = 0;
int finalTime = 0; // To store final time at game over
char display[SCREEN_HEIGHT][SCREEN_WIDTH];
// Function to clear the screen buffer
void clearDisplay() {
memset(display, ' ', sizeof(display));
}
// Function to draw a pixel on the screen
void drawPixel(int x, int y) {
if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) {
display[y][x] = '*';
}
}
// Function to draw a line using Bresenham's algorithm
void drawLine(int x1, int y1, int x2, int y2) {
int dx = abs(x2 - x1), sx = (x1 < x2) ? 1 : -1;
int dy = -abs(y2 - y1), sy = (y1 < y2) ? 1 : -1;
int error = dx + dy, e2;
while (1) {
drawPixel(x1, y1);
if (x1 == x2 && y1 == y2) break;
e2 = 2 * error;
if (e2 >= dy) { error += dy; x1 += sx; }
if (e2 <= dx) { error += dx; y1 += sy; }
}
}
// Function to draw a circle
void drawCircle(int centerX, int centerY, int radius) {
int x = radius - 1, y = 0, dx = 1, dy = 1, err = dx - (radius << 1);
while (x >= y) {
drawPixel(centerX + x, centerY + y);
drawPixel(centerX + y, centerY + x);
drawPixel(centerX - y, centerY + x);
drawPixel(centerX - x, centerY + y);
drawPixel(centerX - x, centerY - y);
drawPixel(centerX - y, centerY - x);
drawPixel(centerX + y, centerY - x);
drawPixel(centerX + x, centerY - y);
if (err <= 0) {
y++;
err += dy;
dy += 2;
}
if (err > 0) {
x--;
dx += 2;
err += dx - (radius << 1);
}
}
}
// Initialize a game object
void initializeGameObject(GameObject *obj, float x, float y, float angle, float radius) {
obj->position = (Vector2){x, y};
obj->velocity = (Vector2){0, 0};
obj->angle = angle;
obj->radius = radius;
}
// Wrap position of the spaceship and asteroids within screen bounds
void wrapPosition(Vector2 *pos) {
if (pos->x < 0) pos->x = SCREEN_WIDTH - 1;
if (pos->x >= SCREEN_WIDTH) pos->x = 0;
if (pos->y < 0) pos->y = SCREEN_HEIGHT - 1;
if (pos->y >= SCREEN_HEIGHT) pos->y = 0;
}
// Check if two game objects are colliding
int checkCollision(GameObject *a, GameObject *b) {
float deltaX = a->position.x - b->position.x;
float deltaY = a->position.y - b->position.y;
return sqrt(deltaX * deltaX + deltaY * deltaY) < (a->radius + b->radius);
}
// Initialize game state
void initGame() {
score = 0; // Reset the score
initializeGameObject(&spaceship, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0, 2);
for (int i = 0; i < MAX_ASTEROIDS; i++) {
initializeGameObject(&asteroids[i],
rand() % SCREEN_WIDTH,
rand() % SCREEN_HEIGHT,
0,
2 + rand() % 3);
asteroids[i].velocity.x = ((float)rand() / RAND_MAX) * 2 - 1;
asteroids[i].velocity.y = ((float)rand() / RAND_MAX) * 2 - 1;
}
for (int i = 0; i < MAX_BULLETS; i++) {
bullets[i].position.x = -1; // Mark bullet as inactive
bullets[i].position.y = -1;
}
startTime = time(NULL);
isGameOver = 0;
finalTime = 0; // Reset final time
}
// Draw the spaceship on the screen
void drawSpaceship() {
int x = (int)spaceship.position.x;
int y = (int)spaceship.position.y;
int size = 3;
float cosAngle = cos(spaceship.angle);
float sinAngle = sin(spaceship.angle);
int x1 = x + size * cosAngle;
int y1 = y + size * sinAngle;
int x2 = x + size * cos(spaceship.angle + 2.5);
int y2 = y + size * sin(spaceship.angle + 2.5);
int x3 = x + size * cos(spaceship.angle - 2.5);
int y3 = y + size * sin(spaceship.angle - 2.5);
drawLine(x1, y1, x2, y2);
drawLine(x2, y2, x3, y3);
drawLine(x3, y3, x1, y1);
}
// Draw all entities on the screen
void drawEntities(GameObject *entities, int count, void (*drawFunc)(GameObject *)) {
for (int i = 0; i < count; i++) {
drawFunc(&entities[i]);
}
}
// Draw a bullet on the screen
void drawBullet(GameObject *bullet) { // Changed to non-const
if (bullet->position.x >= 0) {
drawPixel((int)bullet->position.x, (int)bullet->position.y);
}
}
// Draw an asteroid on the screen
void drawAsteroid(GameObject *asteroid) { // Changed to non-const
drawCircle((int)asteroid->position.x, (int)asteroid->position.y, (int)asteroid->radius);
}
// Refresh the display
void updateDisplay() {
clearDisplay();
if (!isGameOver) {
drawSpaceship();
drawEntities(asteroids, MAX_ASTEROIDS, drawAsteroid);
drawEntities(bullets, MAX_BULLETS, drawBullet);
}
// Print the screen buffer
printf("\033[H");
for (int y = 0; y < SCREEN_HEIGHT; y++) {
for (int x = 0; x < SCREEN_WIDTH; x++) {
putchar(display[y][x]);
}
putchar('\n');
}
// Display score and elapsed time
time_t currentTime = time(NULL);
int elapsedTime = isGameOver ? finalTime : (currentTime - startTime);
printf("Score: %d | Time: %02d:%02d | %s\n", score, elapsedTime / 60, elapsedTime % 60, isGameOver ? "Game Over!" : " ");
}
// Update the position of game objects
void updateGameObject(GameObject *obj, int isBullet) {
obj->position.x += obj->velocity.x;
obj->position.y += obj->velocity.y;
// If it's a bullet, check if it's out of bounds
if (isBullet) {
if (obj->position.x < 0 || obj->position.x >= SCREEN_WIDTH || obj->position.y < 0 || obj->position.y >= SCREEN_HEIGHT) {
obj->position.x = -1; // Deactivate bullet
obj->position.y = -1;
}
} else {
wrapPosition(&obj->position);
}
}
// Update the game state
void updateGame() {
if (isGameOver) return;
// Update spaceship and apply friction
updateGameObject(&spaceship, 0); // 0 indicates it's not a bullet
spaceship.velocity.x *= 0.98;
spaceship.velocity.y *= 0.98;
// Move asteroids and check for collisions
for (int i = 0; i < MAX_ASTEROIDS; i++) {
updateGameObject(&asteroids[i], 0);
if (checkCollision(&spaceship, &asteroids[i])) {
isGameOver = 1;
finalTime = time(NULL) - startTime;
return;
}
}
// Update bullet positions
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x >= 0) {
updateGameObject(&bullets[i], 1); // 1 indicates it's a bullet
}
}
// Check for bullet collisions with asteroids
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x >= 0) {
for (int j = 0; j < MAX_ASTEROIDS; j++) {
if (checkCollision(&bullets[i], &asteroids[j])) {
bullets[i].position.x = -1; // Deactivate bullet
bullets[i].position.y = -1;
asteroids[j].position.x = rand() % SCREEN_WIDTH;
asteroids[j].position.y = rand() % SCREEN_HEIGHT;
score += 100;
}
}
}
}
}
// Fire a bullet
void shootBullet() {
for (int i = 0; i < MAX_BULLETS; i++) {
if (bullets[i].position.x < 0) {
bullets[i].position = spaceship.position;
bullets[i].velocity.x = cos(spaceship.angle) * 2;
bullets[i].velocity.y = sin(spaceship.angle) * 2;
break;
}
}
}
// Check if a key was hit
int isKeyHit() {
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
// Configure terminal settings
void configureTerminal(struct termios *old_tio, struct termios *new_tio) {
tcgetattr(STDIN_FILENO, old_tio);
*new_tio = *old_tio;
new_tio->c_lflag &= (~ICANON & ~ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, new_tio);
}
// Restore terminal settings
void restoreTerminal(struct termios *old_tio) {
tcsetattr(STDIN_FILENO, TCSANOW, old_tio);
}
void onSignal(int sig) {
shouldExit = 1;
}
// Main game loop
int main() {
signal(SIGINT, onSignal); // Capture ^C
srand(time(NULL)); // Seed the random number generator
initGame(); // Initialize the game state
struct termios old_tio, new_tio;
configureTerminal(&old_tio, &new_tio);
printf("\033[?25l"); // Hide the cursor
while (!shouldExit) {
if (isKeyHit()) {
char input = getchar();
if (input == 27) { // ESC key
if (getchar() == '[') { // Handle arrow keys
switch (getchar()) {
case 'A': // Up arrow
spaceship.velocity.x += cos(spaceship.angle) * 0.2;
spaceship.velocity.y += sin(spaceship.angle) * 0.2;
break;
case 'B': // Down arrow
spaceship.velocity.x -= cos(spaceship.angle) * 0.2;
spaceship.velocity.y -= sin(spaceship.angle) * 0.2;
break;
case 'D': spaceship.angle -= 0.2; break; // Left arrow
case 'C': spaceship.angle += 0.2; break; // Right arrow
}
}
} else if (input == ' ') {
shootBullet(); // Fire a bullet
} else if (input == 'q') {
break; // Quit the game
} else if (input == 'r' && isGameOver) {
initGame(); // Restart the game
}
}
updateGame(); // Update game state
updateDisplay(); // Refresh the display
usleep(50000); // Wait for 50ms (20 FPS)
}
printf("\033[?25h"); // Show the cursor
restoreTerminal(&old_tio); // Restore terminal settings
return 0;
}
+15 -5
View File
@@ -49,10 +49,10 @@
* @fileoverview Terminal Screencast Recorder / Player, e.g.
*
* make o//examples/script.com
* o//examples/script.com -r
* o//examples/script.com -w80 -h24 -r recording.tty
* # type stuff..
* # CTRL-D
* o//examples/script.com -p typescript
* o//examples/script.com -p recording.tty
*
* @note works on Linux, OpenBSD, NetBSD, FreeBSD, MacOS
* @see https://asciinema.org/
@@ -103,9 +103,9 @@ main(int argc, char *argv[])
fd_set rfd;
int fm_fd;
int aflg, Fflg, kflg, pflg, ch, k, n;
int flushtime, readstdin;
int flushtime, readstdin, width, height;
aflg = Fflg = kflg = pflg = 0;
aflg = Fflg = kflg = pflg = height = width = 0;
usesleep = 1;
rawout = 0;
flushtime = 30;
@@ -115,7 +115,7 @@ main(int argc, char *argv[])
(void)fm_fd;
while ((ch = getopt(argc, argv, "adeFfkpqrt:")) != -1)
while ((ch = getopt(argc, argv, "adeFfkpqrt:w:h:")) != -1)
switch(ch) {
case 'a':
aflg = 1;
@@ -145,6 +145,12 @@ main(int argc, char *argv[])
if (flushtime < 0)
err(1, "invalid flush time %d", flushtime);
break;
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case '?':
default:
usage();
@@ -172,6 +178,10 @@ main(int argc, char *argv[])
if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
err(1, "openpty");
} else {
if (width)
win.ws_col = width;
if (height)
win.ws_row = height;
if (openpty(&master, &slave, NULL, &tt, &win) == -1)
err(1, "openpty");
ttyflg = 1;
-6
View File
@@ -216,12 +216,6 @@ o//libc/calls/writev.o: private \
-mgeneral-regs-only
# these assembly files are safe to build on aarch64
o/$(MODE)/libc/calls/getcontext.o: libc/calls/getcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
+2
View File
@@ -83,9 +83,11 @@ static void copy_file_range_init(void) {
* @return number of bytes transferred, or -1 w/ errno
* @raise EXDEV if source and destination are on different filesystems
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
* @raise EOPNOTSUPP if filesystem doesn't support this operation
* @raise EPERM if `fdout` refers to an immutable file on Linux
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINVAL if ranges overlap or `flags` is non-zero
* @raise EINVAL on eCryptFs filesystems that have a bug
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
* @raise EFAULT if one of the pointers memory is bad
* @raise ERANGE if overflow happens computing ranges
+8 -13
View File
@@ -122,34 +122,29 @@ textwindows int sys_fstat_nt_handle(int64_t handle, const char16_t *path,
st.st_blksize = 4096;
st.st_gid = st.st_uid = sys_getuid_nt();
// We'll use the "umask" to fake out the mode bits.
uint32_t umask = atomic_load_explicit(&__umask, memory_order_acquire);
switch (GetFileType(handle)) {
case kNtFileTypeUnknown:
break;
case kNtFileTypeChar:
st.st_mode = S_IFCHR | (0666 & ~umask);
st.st_mode = S_IFCHR | 0664;
st.st_dev = 0x66666666;
st.st_ino = handle;
break;
case kNtFileTypePipe:
st.st_mode = S_IFIFO | (0666 & ~umask);
st.st_mode = S_IFIFO | 0664;
st.st_dev = 0x55555555;
st.st_ino = handle;
break;
case kNtFileTypeDisk: {
struct NtByHandleFileInformation wst;
if (GetFileInformationByHandle(handle, &wst)) {
st.st_mode = 0444 & ~umask;
st.st_mode = 0444;
if ((wst.dwFileAttributes & kNtFileAttributeDirectory) ||
IsWindowsExecutable(handle, path)) {
st.st_mode |= 0111 & ~umask;
}
IsWindowsExecutable(handle, path))
st.st_mode |= 0111;
st.st_flags = wst.dwFileAttributes;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
st.st_mode |= 0222 & ~umask;
}
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly))
st.st_mode |= 0220;
if (wst.dwFileAttributes & kNtFileAttributeReparsePoint) {
st.st_mode |= S_IFLNK;
} else if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
@@ -195,7 +190,7 @@ textwindows int sys_fstat_nt_handle(int64_t handle, const char16_t *path,
} else if (GetVolumeInformationByHandle(
handle, 0, 0, &wst.dwVolumeSerialNumber, 0, 0, 0, 0)) {
st.st_dev = wst.dwVolumeSerialNumber;
st.st_mode = S_IFDIR | (0444 & ~umask);
st.st_mode = S_IFDIR | 0555;
} else {
// both GetFileInformationByHandle and
// GetVolumeInformationByHandle failed
-1
View File
@@ -21,7 +21,6 @@
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/stdckdint.h"
#include "libc/sysv/errfuns.h"
/**
+1
View File
@@ -30,6 +30,7 @@ int CountConsoleInputBytes(void);
int FlushConsoleInputBytes(void);
int64_t GetConsoleInputHandle(void);
int64_t GetConsoleOutputHandle(void);
void EchoConsoleNt(const char *, size_t, bool);
int IsWindowsExecutable(int64_t, const char16_t *);
void InterceptTerminalCommands(const char *, size_t);
+2
View File
@@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_MAKEDEV_H_
#define COSMOPOLITAN_LIBC_CALLS_MAKEDEV_H_
COSMOPOLITAN_C_START_
uint64_t makedev(uint32_t, uint32_t) libcesque;
uint32_t major(uint64_t) libcesque;
@@ -9,4 +10,5 @@ uint32_t minor(uint64_t) libcesque;
#define minor(x) minor(x)
#define makedev(x, y) makedev(x, y)
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_MAKEDEV_H_ */
+1 -1
View File
@@ -57,7 +57,7 @@ textwindows static int _park_thread(uint32_t msdelay, sigset_t waitmask,
CloseHandle(sigev);
// recursion is now safe
if (ws == -1)
if (ws == -1u)
return __winerr();
int handler_was_called = 0;
if (sig)
-3
View File
@@ -56,7 +56,6 @@ static textwindows int sys_pipe_nt_impl(int pipefd[2], unsigned flags) {
__fds_unlock();
hin = CreateNamedPipe(pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped,
mode, 1, PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable);
__fds_lock();
if (hin != -1) {
if ((hout = CreateFile(
pipename, kNtGenericWrite,
@@ -73,7 +72,6 @@ static textwindows int sys_pipe_nt_impl(int pipefd[2], unsigned flags) {
g_fds.p[writer].handle = hout;
pipefd[0] = reader;
pipefd[1] = writer;
__fds_unlock();
return 0;
} else {
CloseHandle(hin);
@@ -81,7 +79,6 @@ static textwindows int sys_pipe_nt_impl(int pipefd[2], unsigned flags) {
}
__releasefd(writer);
__releasefd(reader);
__fds_unlock();
return -1;
}
+37 -14
View File
@@ -17,6 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/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.internal.h"
@@ -25,6 +26,7 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/filetype.h"
@@ -41,6 +43,7 @@
#include "libc/sock/internal.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
@@ -84,7 +87,7 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
struct PosixThread *pt;
int i, rc, ev, kind, gotsocks;
struct sys_pollfd_nt sockfds[64];
uint32_t cm, fi, wi, sn, pn, avail, waitfor, already_slept;
uint32_t cm, fi, sn, pn, avail, waitfor, already_slept;
// ensure revents is cleared
for (i = 0; i < nfds; ++i)
@@ -228,22 +231,36 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
// this ensures low latency for apps like emacs which with no sock
// here we shall actually report that something can be written too
if (!already_slept) {
if (__sigcheck(waitmask, false))
return -1;
intptr_t sigev;
if (!(sigev = CreateEvent(0, 0, 0, 0)))
return __winerr();
filehands[pn] = sigev;
pt = _pthread_self();
filehands[pn] = pt->pt_event = CreateEvent(0, 0, 0, 0);
pt->pt_event = sigev;
pt->pt_blkmask = waitmask;
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT,
memory_order_release);
wi = WaitForMultipleObjects(pn + 1, filehands, 0, waitfor);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
int sig = 0;
uint32_t wi = pn;
if (!_is_canceled() &&
!(_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))))
wi = WaitForMultipleObjects(pn + 1, filehands, 0, waitfor);
//!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!/!//
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
CloseHandle(filehands[pn]);
if (wi == -1u) {
CloseHandle(sigev);
if (wi == -1u)
// win32 wait failure
return __winerr();
} else if (wi == pn) {
if (wi == pn) {
// our signal event was signalled
if (__sigcheck(waitmask, false))
int handler_was_called = 0;
if (sig)
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1)
return -1;
if (handler_was_called)
return eintr();
} else if ((wi ^ kNtWaitAbandoned) < pn) {
// this is possibly because a process or thread was killed
fds[fileindices[wi ^ kNtWaitAbandoned]].revents = POLLERR_;
@@ -287,8 +304,6 @@ textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
} else {
// should only be possible on kNtWaitTimeout or semaphore abandoned
// keep looping for events and we'll catch timeout when appropriate
if (__sigcheck(waitmask, false))
return -1;
}
}
@@ -306,6 +321,13 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
uint32_t waitms;
int i, n, rc, got = 0;
// 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
// ppoll is a function where users are surely thinking about signals,
// since ppoll actually allows them to block signals everywhere else.
if (__sigcheck(waitmask, false))
return -1;
// fast path
if (nfds <= 63)
return sys_poll_nt_actual(fds, nfds, deadline, waitmask);
@@ -329,13 +351,14 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
}
}
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds,
const struct timespec *relative,
const sigset_t *sigmask) {
int rc;
struct timespec now, timeout, deadline;
BLOCK_SIGNALS;
now = ms ? sys_clock_gettime_monotonic_nt() : timespec_zero;
timeout = ms ? timespec_frommillis(*ms) : timespec_max;
now = relative ? sys_clock_gettime_monotonic_nt() : timespec_zero;
timeout = relative ? *relative : timespec_max;
deadline = timespec_add(now, timeout);
rc = sys_poll_nt_impl(fds, nfds, deadline, sigmask ? *sigmask : _SigMask);
ALLOW_SIGNALS;
+8 -12
View File
@@ -25,6 +25,7 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/strace.h"
#include "libc/limits.h"
#include "libc/runtime/stack.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sock/struct/pollfd.internal.h"
@@ -76,10 +77,13 @@ static int ppoll_impl(struct pollfd *fds, size_t nfds,
}
fdcount = sys_ppoll(fds, nfds, tsp, sigmask, 8);
if (fdcount == -1 && errno == ENOSYS) {
int ms;
int64_t ms;
errno = e;
if (!timeout || ckd_add(&ms, timeout->tv_sec,
(timeout->tv_nsec + 999999) / 1000000)) {
if (timeout) {
ms = timespec_tomillis(*timeout);
if (ms > INT_MAX)
ms = -1;
} else {
ms = -1;
}
if (sigmask)
@@ -89,15 +93,7 @@ static int ppoll_impl(struct pollfd *fds, size_t nfds,
sys_sigprocmask(SIG_SETMASK, &oldmask, 0);
}
} else {
uint32_t ms;
uint32_t *msp;
if (timeout &&
!ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
msp = &ms;
} else {
msp = 0;
}
fdcount = sys_poll_nt(fds, nfds, msp, sigmask);
fdcount = sys_poll_nt(fds, nfds, timeout, sigmask);
}
if (IsOpenbsd() && fdcount != -1) {
+4 -12
View File
@@ -67,7 +67,6 @@
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
const struct timespec *timeout, const sigset_t *sigmask) {
int rc;
struct timeval tv, *tvp;
struct timespec ts, *tsp;
struct {
const sigset_t *s;
@@ -111,24 +110,17 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
rc = sys_pselect(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeout, sigmask);
} else {
if (timeout) {
tv.tv_sec = timeout->tv_sec;
tv.tv_usec = timeout->tv_nsec / 1000;
tvp = &tv;
} else {
tvp = 0;
}
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask);
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, timeout, sigmask);
}
}
END_CANCELATION_POINT;
STRACE("pselect(%d, %s → [%s], %s → [%s], %s → [%s], %s, %s) → %d% m", nfds,
DescribeFdSet(rc, nfds, old_readfds_ptr),
DescribeFdSet(0, nfds, old_readfds_ptr),
DescribeFdSet(rc, nfds, readfds),
DescribeFdSet(rc, nfds, old_writefds_ptr),
DescribeFdSet(0, nfds, old_writefds_ptr),
DescribeFdSet(rc, nfds, writefds),
DescribeFdSet(rc, nfds, old_exceptfds_ptr),
DescribeFdSet(0, nfds, old_exceptfds_ptr),
DescribeFdSet(rc, nfds, exceptfds), //
DescribeTimespec(0, timeout), //
DescribeSigset(0, sigmask), rc);
+7 -3
View File
@@ -320,9 +320,12 @@ textwindows static int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
// note we define _POSIX_VDISABLE as zero
// tcsetattr() lets anyone reconfigure these keybindings
if (c && !(__ttyconf.magic & kTtyNoIsigs) && !__keystroke.bypass_mode) {
char b[] = {c};
if (c == __ttyconf.vintr) {
EchoConsoleNt(b, 1, false);
return AddSignal(SIGINT);
} else if (c == __ttyconf.vquit) {
EchoConsoleNt(b, 1, false);
return AddSignal(SIGQUIT);
}
}
@@ -457,7 +460,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 & kTtyEchoRaw) {
WriteTty(p, n);
@@ -517,7 +521,7 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
memcpy(k->buf, buf, sizeof(k->buf));
k->buflen = len;
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;
@@ -616,7 +620,7 @@ textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
// echo input if it was successfully recorded
// assuming the win32 console isn't doing it already
EchoTty(buf, len, false);
EchoConsoleNt(buf, len, false);
// save keystroke to appropriate list
if (__ttyconf.magic & kTtyUncanon) {
+2 -20
View File
@@ -25,7 +25,6 @@
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sock/struct/pollfd.internal.h"
#include "libc/stdckdint.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
@@ -44,7 +43,7 @@
// </sync libc/sysv/consts.sh>
int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask) {
int pfds = 0;
@@ -68,21 +67,8 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
}
}
// convert the wait time to a word
uint32_t millis;
if (!timeout) {
millis = -1u;
} else {
int64_t ms = timeval_tomillis(*timeout);
if (ms < 0 || ms > UINT32_MAX) {
millis = -1u;
} else {
millis = ms;
}
}
// call our nt poll implementation
int fdcount = sys_poll_nt(fds, pfds, &millis, sigmask);
int fdcount = sys_poll_nt(fds, pfds, timeout, sigmask);
if (fdcount == -1)
return -1;
@@ -115,10 +101,6 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
}
}
// store remaining time back in caller's timeval
if (timeout)
*timeout = timeval_frommillis(millis);
return bits;
}
+1 -4
View File
@@ -31,10 +31,7 @@
* such as out-of-band data on a socket; it is equivalent to POLLPRI
* in the revents of poll()
* @param timeout may be null which means to block indefinitely; cosmo's
* implementation of select() never modifies this parameter which is
* how most platforms except Linux work which modifies it to reflect
* elapsed time, noting that POSIX permits either behavior therefore
* portable code should assume that timeout memory becomes undefined
* implementation of select() never modifies this parameter
* @raise E2BIG if we exceeded the 64 socket limit on Windows
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
-1
View File
@@ -21,7 +21,6 @@
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/stdckdint.h"
#include "libc/sysv/errfuns.h"
/**
-662
View File
@@ -1,662 +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 void __sig_wake(struct PosixThread *pt, int sig) {
atomic_int *blocker;
blocker = atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
if (!blocker)
return;
// 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;
}
// 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);
}
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. we also
// need to ensure we don't overflow the target thread's stack if many
// signals need to be delivered at once. we also need to make sure two
// threads can't deadlock by killing each other at the same time.
if ((atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1))) ||
atomic_exchange_explicit(&pt->pt_intoff, 1, memory_order_acquire)) {
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
memory_order_relaxed);
__sig_wake(pt, sig);
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);
}
// ignore signals already pending
uintptr_t th = _pthread_syshand(pt);
if (atomic_load_explicit(&pt->tib->tib_sigpending, memory_order_acquire) &
(1ull << (sig - 1))) {
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
return 0;
}
// take control of thread
// suspending the thread happens asynchronously
// however getting the context blocks until it's frozen
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 ((atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
(1ull << (sig - 1))) ||
!((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;
}
BLOCK_SIGNALS;
_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);
}
ALLOW_SIGNALS;
}
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);
}
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__ */
-71
View File
@@ -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_ */
+9 -5
View File
@@ -34,10 +34,14 @@
* @norestart
*/
int usleep(uint64_t micros) {
errno_t err;
struct timespec ts = timespec_frommicros(micros);
err = clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0);
if (err)
return errno = err, -1;
// All OSes except OpenBSD return instantly on usleep(0). So we might
// as well avoid system call overhead and helping OpenBSD work better
if (micros) {
errno_t err;
struct timespec ts = timespec_frommicros(micros);
err = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, 0);
if (err)
return errno = err, -1;
}
return 0;
}
+5
View File
@@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_COSMO_H_
#define COSMOPOLITAN_LIBC_COSMO_H_
#include "libc/calls/struct/timespec.h"
COSMOPOLITAN_C_START_
#ifndef __cplusplus
@@ -16,6 +17,10 @@ int __demangle(char *, const char *, size_t) libcesque;
int __is_mangled(const char *) libcesque;
bool32 IsLinuxModern(void) libcesque;
int LoadZipArgs(int *, char ***) libcesque;
int cosmo_args(const char *, char ***) libcesque;
int cosmo_futex_wake(_COSMO_ATOMIC(int) *, int, char);
int cosmo_futex_wait(_COSMO_ATOMIC(int) *, int, char, int,
const struct timespec *);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_COSMO_H_ */
+1 -1
View File
@@ -4,7 +4,7 @@
#define __COSMOPOLITAN_MAJOR__ 3
#define __COSMOPOLITAN_MINOR__ 9
#define __COSMOPOLITAN_PATCH__ 2
#define __COSMOPOLITAN_PATCH__ 7
#define __COSMOPOLITAN__ \
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
__COSMOPOLITAN_PATCH__)
+20 -2
View File
@@ -30,9 +30,11 @@ LIBC_INTRIN_A_CHECKS = \
LIBC_INTRIN_A_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_NT_KERNEL32 \
LIBC_NT_REALTIME \
LIBC_NT_SYNCHRONIZATION \
LIBC_NT_WS2_32 \
LIBC_SYSV \
LIBC_SYSV_CALLS
LIBC_SYSV_CALLS \
LIBC_INTRIN_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x))))
@@ -106,14 +108,30 @@ o//libc/intrin/demangle.o: private \
CFLAGS += \
-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
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
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/intrin/fenv.o: libc/intrin/fenv.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/intrin/gcov.o: libc/intrin/gcov.S
@$(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 $<
o/$(MODE)/libc/intrin/typeinfo.o: libc/intrin/typeinfo.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -16,18 +16,14 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/sysv/consts/futex.h"
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/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.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
@@ -37,62 +33,56 @@
#include "libc/intrin/ulock.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/nexgen32e/vendor.internal.h"
#include "libc/nt/runtime.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/timer.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/freebsd.internal.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#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"
// clang-format off
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
errno_t _futex (atomic_int *, int, int, const struct timespec *, int *, int);
errno_t _futex_wake (atomic_int *, int, int) asm ("_futex");
errno_t cosmo_futex_thunk (atomic_int *, int, int, const struct timespec *, int *, int);
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);
static struct NsyncFutex {
static struct CosmoFutex {
atomic_uint once;
int FUTEX_WAIT_;
int FUTEX_PRIVATE_FLAG_;
int FUTEX_CLOCK_REALTIME_;
bool is_supported;
bool timeout_is_relative;
} nsync_futex_;
} g_cosmo_futex;
static void nsync_futex_init_ (void) {
static void cosmo_futex_init (void) {
int e;
atomic_int x;
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT;
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
if (IsWindows ()) {
nsync_futex_.is_supported = true;
g_cosmo_futex.is_supported = true;
return;
}
if (IsXnu ()) {
nsync_futex_.is_supported = true;
nsync_futex_.timeout_is_relative = true;
g_cosmo_futex.is_supported = true;
g_cosmo_futex.timeout_is_relative = true;
return;
}
if (IsFreebsd ()) {
nsync_futex_.is_supported = true;
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
g_cosmo_futex.is_supported = true;
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
return;
}
if (!(nsync_futex_.is_supported = IsLinux () || IsOpenbsd ()))
if (!(g_cosmo_futex.is_supported = IsLinux () || IsOpenbsd ()))
return;
// In our testing, we found that the monotonic clock on various
@@ -100,7 +90,7 @@ static void nsync_futex_init_ (void) {
// better behaved than the realtime clock, and routinely took
// large steps backwards, especially on multiprocessors. Given
// 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
// configuring any time synchronization mechanism (like ntp) to
// adjust for leap seconds by adjusting the rate, rather than
@@ -108,31 +98,32 @@ static void nsync_futex_init_ (void) {
e = errno;
atomic_store_explicit (&x, 0, memory_order_relaxed);
if (IsLinux () &&
_futex (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME,
1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT_BITSET;
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
nsync_futex_.FUTEX_CLOCK_REALTIME_ = FUTEX_CLOCK_REALTIME;
cosmo_futex_thunk (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME,
1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT_BITSET;
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
g_cosmo_futex.FUTEX_CLOCK_REALTIME_ = FUTEX_CLOCK_REALTIME;
} else if (IsOpenbsd () ||
(IsLinux () &&
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT;
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
nsync_futex_.timeout_is_relative = true;
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
g_cosmo_futex.timeout_is_relative = true;
} else {
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT;
nsync_futex_.timeout_is_relative = true;
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
g_cosmo_futex.timeout_is_relative = true;
}
errno = e;
}
static uint32_t nsync_time_64to32u (uint64_t duration) {
static uint32_t cosmo_time_64to32u (uint64_t duration) {
if (duration <= -1u)
return duration;
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 (;;) {
if (atomic_load_explicit (w, memory_order_acquire) != expect)
return 0;
@@ -148,10 +139,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,
int clock, const struct timespec *timeout,
struct PosixThread *pt,
sigset_t waitmask) {
static int cosmo_futex_wait_win32 (atomic_int *w, int expect, char pshare,
int clock, const struct timespec *timeout,
struct PosixThread *pt,
sigset_t waitmask) {
#ifdef __x86_64__
int sig;
bool32 ok;
@@ -183,7 +174,7 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
pt->pt_blkmask = waitmask;
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) {
/* __sig_wake wakes our futex without changing `w` after enqueing signals */
atomic_store_explicit (&pt->pt_blocker, 0, memory_order_release);
@@ -197,7 +188,7 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
if (ok) {
return 0;
} else {
ASSERT (GetLastError () == ETIMEDOUT);
unassert (GetLastError () == ETIMEDOUT);
}
}
#else
@@ -205,14 +196,14 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
#endif /* __x86_64__ */
}
static int nsync_futex_fix_timeout_ (struct timespec *memory, int clock,
const struct timespec *abstime,
struct timespec **result) {
static int cosmo_futex_fix_timeout (struct timespec *memory, int clock,
const struct timespec *abstime,
struct timespec **result) {
struct timespec now;
if (!abstime) {
*result = 0;
return 0;
} else if (!nsync_futex_.timeout_is_relative) {
} else if (!g_cosmo_futex.timeout_is_relative) {
*memory = *abstime;
*result = memory;
return 0;
@@ -225,22 +216,39 @@ 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
*/
int cosmo_futex_wait (atomic_int *w, int expect, char pshare,
int clock, const struct timespec *abstime) {
int e, rc, op;
struct CosmoTib *tib;
struct PosixThread *pt;
struct timespec tsmem;
struct timespec *timeout = 0;
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)
op |= nsync_futex_.FUTEX_PRIVATE_FLAG_;
op |= g_cosmo_futex.FUTEX_PRIVATE_FLAG_;
if (clock == CLOCK_REALTIME ||
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) {
rc = -ETIMEDOUT;
@@ -252,7 +260,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
goto Finished;
}
if ((rc = nsync_futex_fix_timeout_ (&tsmem, clock, abstime, &timeout)))
if ((rc = cosmo_futex_fix_timeout (&tsmem, clock, abstime, &timeout)))
goto Finished;
LOCKTRACE ("futex(%t [%d], %s, %#x, %s) → ...",
@@ -263,13 +271,13 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
tib = __get_tls();
pt = (struct PosixThread *)tib->tib_pthread;
if (nsync_futex_.is_supported) {
if (g_cosmo_futex.is_supported) {
e = errno;
if (IsWindows ()) {
// Windows 8 futexes don't support multiple processes :(
if (pshare) goto Polyfill;
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);
} else if (IsXnu ()) {
@@ -293,7 +301,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
op = UL_COMPARE_AND_WAIT;
}
if (timeout) {
us = nsync_time_64to32u (timespec_tomicros (*timeout));
us = cosmo_time_64to32u (timespec_tomicros (*timeout));
} else {
us = -1u;
}
@@ -333,7 +341,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
}
} else {
Polyfill:
rc = nsync_futex_polyfill_ (w, expect, clock, timeout);
rc = cosmo_futex_polyfill (w, expect, clock, timeout);
}
Finished:
@@ -346,18 +354,24 @@ Finished:
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;
ASSERT (count == 1 || count == INT_MAX);
cosmo_once (&nsync_futex_.once, nsync_futex_init_);
cosmo_once (&g_cosmo_futex.once, cosmo_futex_init);
op = FUTEX_WAKE;
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 (pshare) {
goto Polyfill;
@@ -379,7 +393,7 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
op |= ULF_WAKE_ALL;
}
rc = ulock_wake (op, w, 0);
ASSERT (!rc || rc == -ENOENT);
unassert (!rc || rc == -ENOENT);
if (!rc) {
rc = 1;
} else if (rc == -ENOENT) {
@@ -20,7 +20,7 @@
#include "libc/macros.h"
.privileged
_futex:
cosmo_futex_thunk:
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
@@ -47,4 +47,4 @@ _futex:
#error "unsupported architecture"
#endif /* __x86_64__ */
1: ret
.endfn _futex,globl,hidden
.endfn cosmo_futex_thunk,globl,hidden
+2
View File
@@ -31,6 +31,8 @@ const char *_DescribeFdSet(char buf[N], ssize_t rc, int nfds, fd_set *fds) {
if (!fds)
return "NULL";
if (rc == -1)
return "n/a";
if (kisdangerous(fds)) {
ksnprintf(buf, N, "%p", fds);
return buf;
+7 -1
View File
@@ -129,6 +129,7 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
if (IsWindows()) {
const char *fdspec;
if ((fdspec = getenv("_COSMO_FDS_V2"))) {
char *smaddr = 0;
unsetenv("_COSMO_FDS");
unsetenv("_COSMO_FDS_V2");
for (;;) {
@@ -171,8 +172,13 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
if (shand) {
struct Map *map;
struct CursorShared *shared;
if (!smaddr) {
smaddr = __maps_randaddr();
} else {
smaddr += 65536;
}
if ((shared = MapViewOfFileEx(shand, kNtFileMapWrite, 0, 0,
sizeof(struct CursorShared), 0))) {
sizeof(struct CursorShared), smaddr))) {
if ((f->cursor = _mapanon(sizeof(struct Cursor)))) {
f->cursor->shared = shared;
if ((map = __maps_alloc())) {
@@ -27,6 +27,6 @@
.ftrace1
getcontext:
.ftrace2
#include "libc/calls/getcontext.inc"
#include "libc/intrin/getcontext.inc"
jmp __getcontextsig
.endfn getcontext,globl
@@ -34,6 +34,7 @@
mov %rbp,120(%rdi)
mov %rbx,128(%rdi)
mov %rdx,136(%rdi)
mov %rax,144(%rdi)
mov %rcx,152(%rdi)
lea 8(%rsp),%rax
mov %rax,160(%rdi) // rsp = caller's rsp
+19 -6
View File
@@ -40,9 +40,11 @@
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/errors.h"
#include "libc/nt/events.h"
#include "libc/nt/files.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/overlapped.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h"
@@ -113,10 +115,13 @@
}
// clang-format off
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
__msabi extern typeof(CreateEvent) *const __imp_CreateEventW;
__msabi extern typeof(CreateFile) *const __imp_CreateFileW;
__msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle;
__msabi extern typeof(GetEnvironmentVariable) *const __imp_GetEnvironmentVariableW;
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
__msabi extern typeof(GetOverlappedResult) *const __imp_GetOverlappedResult;
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
__msabi extern typeof(SetLastError) *const __imp_SetLastError;
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
@@ -283,7 +288,7 @@ privileged long kloghandle(void) {
hand = __imp_CreateFileW(
path, kNtFileAppendData,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
kNtOpenAlways, kNtFileAttributeNormal, 0);
kNtOpenAlways, kNtFileAttributeNormal | kNtFileFlagOverlapped, 0);
} else {
hand = -1; // KPRINTF_LOG was empty string or too long
}
@@ -359,7 +364,6 @@ privileged void _klog_serial(const char *b, size_t n) {
privileged void klog(const char *b, size_t n) {
#ifdef __x86_64__
int e;
long h;
uint32_t wrote;
long rax, rdi, rsi, rdx;
@@ -367,11 +371,20 @@ privileged void klog(const char *b, size_t n) {
return;
}
if (IsWindows()) {
e = __imp_GetLastError();
if (!__imp_WriteFile(h, b, n, &wrote, 0)) {
__imp_SetLastError(e);
__klog_handle = 0;
bool32 ok;
intptr_t ev;
int e = __imp_GetLastError();
if ((ev = __imp_CreateEventW(0, 0, 0, 0))) {
struct NtOverlapped overlap = {.hEvent = ev};
ok = !!__imp_WriteFile(h, b, n, 0, &overlap);
if (!ok && __imp_GetLastError() == kNtErrorIoPending)
ok = true;
ok &= !!__imp_GetOverlappedResult(h, &overlap, &wrote, true);
if (!ok)
__klog_handle = 0;
__imp_CloseHandle(ev);
}
__imp_SetLastError(e);
} else if (IsMetal()) {
if (_weaken(_klog_vga)) {
_weaken(_klog_vga)(b, n);
+1 -2
View File
@@ -32,7 +32,7 @@ struct Maps {
_Atomic(uintptr_t) freed;
size_t count;
size_t pages;
_Atomic(char *) pick;
char *pick;
struct Map stack;
struct Map guard;
};
@@ -49,7 +49,6 @@ bool __maps_lock(void);
void __maps_check(void);
void __maps_unlock(void);
void *__maps_randaddr(void);
void *__maps_pickaddr(size_t);
void __maps_add(struct Map *);
void __maps_free(struct Map *);
void __maps_insert(struct Map *);
+22 -17
View File
@@ -19,10 +19,10 @@
#include "libc/dce.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#include "third_party/aarch64/arm_neon.internal.h"
#include "third_party/intel/immintrin.internal.h"
#ifndef __aarch64__
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1)));
static inline const unsigned char *memchr_pure(const unsigned char *s,
unsigned char c, size_t n) {
size_t i;
@@ -35,22 +35,27 @@ static inline const unsigned char *memchr_pure(const unsigned char *s,
}
#if defined(__x86_64__) && !defined(__chibicc__)
static __vex const unsigned char *memchr_sse(const unsigned char *s,
unsigned char c, size_t n) {
size_t i;
unsigned m;
xmm_t v, t = {c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c};
for (; n >= 16; n -= 16, s += 16) {
v = *(const xmm_t *)s;
m = __builtin_ia32_pmovmskb128(v == t);
if (m) {
m = __builtin_ctzll(m);
return s + m;
}
static const char *memchr_sse(const char *s, char c, size_t n) {
const char *e = s + n;
__m128i t = _mm_set1_epi8(c);
unsigned m, k = (uintptr_t)s & 15;
m = _mm_movemask_epi8(
_mm_cmpeq_epi8(_mm_load_si128((const __m128i *)((uintptr_t)s & -16)), t));
m >>= k;
if (m) {
s += __builtin_ctz(m);
if (s < e)
return s;
return 0;
}
for (i = 0; i < n; ++i) {
if (s[i] == c) {
return s + i;
for (s += 16 - k; s < e; s += 16) {
m = _mm_movemask_epi8(
_mm_cmpeq_epi8(_mm_load_si128((const __m128i *)s), t));
if (m) {
s += __builtin_ctz(m);
if (s < e)
return s;
return 0;
}
}
return 0;
+8 -9
View File
@@ -411,22 +411,21 @@ void *__maps_randaddr(void) {
return (void *)addr;
}
void *__maps_pickaddr(size_t size) {
static void *__maps_pickaddr(size_t size) {
char *addr;
__maps_lock();
for (int try = 0; try < MAX_TRIES; ++try) {
addr = atomic_exchange_explicit(&__maps.pick, 0, memory_order_acq_rel);
addr = __maps.pick;
__maps.pick = 0;
if (!addr)
addr = __maps_randaddr();
__maps_lock();
bool overlaps = __maps_overlaps(addr, size, __pagesize);
__maps_unlock();
if (!overlaps) {
atomic_store_explicit(&__maps.pick,
addr + ((size + __gransize - 1) & __gransize),
memory_order_release);
if (!__maps_overlaps(addr, size, __pagesize)) {
__maps.pick = addr + ((size + __gransize - 1) & -__gransize);
__maps_unlock();
return addr;
}
}
__maps_unlock();
return 0;
}
+65
View File
@@ -0,0 +1,65 @@
/*-*- 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/fmt/conv.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.h"
#include "libc/nt/enum/memflags.h"
#include "libc/nt/memory.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
static const struct DescribeFlags kNtMemState[] = {
{kNtMemCommit, "Commit"}, //
{kNtMemFree, "Free"}, //
{kNtMemReserve, "Reserve"}, //
};
const char *DescribeNtMemState(char buf[64], uint32_t x) {
return _DescribeFlags(buf, 64, kNtMemState, ARRAYLEN(kNtMemState), "kNtMem",
x);
}
static const struct DescribeFlags kNtMemType[] = {
{kNtMemImage, "Image"}, //
{kNtMemMapped, "Mapped"}, //
{kNtMemPrivate, "Private"}, //
};
const char *DescribeNtMemType(char buf[64], uint32_t x) {
return _DescribeFlags(buf, 64, kNtMemType, ARRAYLEN(kNtMemType), "kNtMem", x);
}
void __print_maps_win32(void) {
char *p, b[5][64];
struct NtMemoryBasicInformation mi;
kprintf("%-12s %-12s %10s %16s %16s %32s %32s\n", "Allocation", "BaseAddress",
"RegionSize", "State", "Type", "AllocationProtect", "Protect");
for (p = 0;; p = (char *)mi.BaseAddress + mi.RegionSize) {
bzero(&mi, sizeof(mi));
if (!VirtualQuery(p, &mi, sizeof(mi)))
break;
sizefmt(b[0], mi.RegionSize, 1024);
kprintf("%.12lx %.12lx %10s %16s %16s %32s %32s\n", mi.AllocationBase,
mi.BaseAddress, b[0], DescribeNtMemState(b[1], mi.State),
DescribeNtMemType(b[2], mi.Type),
_DescribeNtPageFlags(b[3], mi.AllocationProtect),
_DescribeNtPageFlags(b[4], mi.Protect));
}
}
+3 -23
View File
@@ -19,6 +19,7 @@
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
@@ -28,25 +29,8 @@
#include "libc/runtime/internal.h"
#include "libc/thread/lock.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/futex.internal.h"
#include "third_party/nsync/mu.h"
static void pthread_mutex_lock_spin(atomic_int *word) {
int backoff = 0;
if (atomic_exchange_explicit(word, 1, memory_order_acquire)) {
LOCKTRACE("acquiring pthread_mutex_lock_spin(%t)...", word);
for (;;) {
for (;;) {
if (!atomic_load_explicit(word, memory_order_relaxed))
break;
backoff = pthread_delay_np(word, backoff);
}
if (!atomic_exchange_explicit(word, 1, memory_order_acquire))
break;
}
}
}
// see "take 3" algorithm in "futexes are tricky" by ulrich drepper
// slightly improved to attempt acquiring multiple times b4 syscall
static void pthread_mutex_lock_drepper(atomic_int *futex, char pshare) {
@@ -59,7 +43,7 @@ static void pthread_mutex_lock_drepper(atomic_int *futex, char pshare) {
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
BLOCK_CANCELATION;
while (word > 0) {
_weaken(nsync_futex_wait_)(futex, 2, pshare, 0, 0);
cosmo_futex_wait(futex, 2, pshare, 0, 0);
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
}
ALLOW_CANCELATION;
@@ -164,11 +148,7 @@ static errno_t pthread_mutex_lock_impl(pthread_mutex_t *mutex) {
// handle normal mutexes
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
if (_weaken(nsync_futex_wait_)) {
pthread_mutex_lock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
} else {
pthread_mutex_lock_spin(&mutex->_futex);
}
pthread_mutex_lock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
return 0;
}
+2 -14
View File
@@ -24,15 +24,8 @@
#include "libc/runtime/internal.h"
#include "libc/thread/lock.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/futex.internal.h"
#include "third_party/nsync/mu.h"
static errno_t pthread_mutex_trylock_spin(atomic_int *word) {
if (!atomic_exchange_explicit(word, 1, memory_order_acquire))
return 0;
return EBUSY;
}
static errno_t pthread_mutex_trylock_drepper(atomic_int *futex) {
int word = 0;
if (atomic_compare_exchange_strong_explicit(
@@ -142,13 +135,8 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
#endif
// handle normal mutexes
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
if (_weaken(nsync_futex_wait_)) {
return pthread_mutex_trylock_drepper(&mutex->_futex);
} else {
return pthread_mutex_trylock_spin(&mutex->_futex);
}
}
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL)
return pthread_mutex_trylock_drepper(&mutex->_futex);
// handle recursive and error checking mutexes
#if PTHREAD_USE_NSYNC
+3 -11
View File
@@ -18,6 +18,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
@@ -26,19 +27,14 @@
#include "libc/runtime/internal.h"
#include "libc/thread/lock.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/futex.internal.h"
#include "third_party/nsync/mu.h"
static void pthread_mutex_unlock_spin(atomic_int *word) {
atomic_store_explicit(word, 0, memory_order_release);
}
// see "take 3" algorithm in "futexes are tricky" by ulrich drepper
static void pthread_mutex_unlock_drepper(atomic_int *futex, char pshare) {
int word = atomic_fetch_sub_explicit(futex, 1, memory_order_release);
if (word == 2) {
atomic_store_explicit(futex, 0, memory_order_release);
_weaken(nsync_futex_wake_)(futex, 1, pshare);
cosmo_futex_wake(futex, 1, pshare);
}
}
@@ -137,11 +133,7 @@ errno_t pthread_mutex_unlock(pthread_mutex_t *mutex) {
// implement barebones normal mutexes
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
if (_weaken(nsync_futex_wake_)) {
pthread_mutex_unlock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
} else {
pthread_mutex_unlock_spin(&mutex->_futex);
}
pthread_mutex_unlock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
return 0;
}
File renamed without changes.
+708 -21
View File
@@ -1,7 +1,7 @@
/*-*- 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 2023 Justine Alexandra Roberts Tunney │
│ 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 │
@@ -17,37 +17,724 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/sysv/consts/sig.h"
#include "ape/sections.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.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/nomultics.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/tls.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/sysv/consts/termios.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
struct Signals __sig;
/**
* @fileoverview Cosmopolitan Signals for Windows.
*/
sigset_t __sig_block(void) {
if (IsWindows() || IsMetal()) {
if (__tls_enabled)
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, -1,
memory_order_acquire);
else
#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;
} else {
sigset_t res, neu = -1;
sys_sigprocmask(SIG_SETMASK, &neu, &res);
return res;
}
}
}
void __sig_unblock(sigset_t m) {
if (IsWindows() || IsMetal()) {
if (__tls_enabled) {
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
if (_weaken(__sig_check))
_weaken(__sig_check)();
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;
}
} else {
sys_sigprocmask(SIG_SETMASK, &m, 0);
}
}
// 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;
}
}
static textwindows int __sig_console_char(uint32_t dwCtrlType) {
switch (dwCtrlType) {
case kNtCtrlCEvent:
return __ttyconf.vintr;
case kNtCtrlBreakEvent:
return __ttyconf.vquit;
default:
return _POSIX_VDISABLE;
}
}
__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));
// ensure that ^C or ^\ gets printed to console appropriately
if (_weaken(EchoConsoleNt)) {
char c;
if ((c = __sig_console_char(dwCtrlType)) != _POSIX_VDISABLE)
_weaken(EchoConsoleNt)(&c, sizeof(c), false);
}
// take control of random thread and inject call to signal handler
__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__ */
@@ -16,26 +16,38 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/testlib/testlib.h"
#include "libc/sysv/consts/sig.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/tls.h"
void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown();
struct Signals __sig;
sigset_t __sig_block(void) {
if (IsWindows() || IsMetal()) {
if (__tls_enabled)
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, -1,
memory_order_acquire);
else
return 0;
} else {
sigset_t res, neu = -1;
sys_sigprocmask(SIG_SETMASK, &neu, &res);
return res;
}
}
uint32_t GetMode(int fd) {
struct stat st;
ASSERT_SYS(0, 0, fstat(fd, &st));
return st.st_mode & 0777;
}
TEST(fchmod, canChangeReadOnlyBit) {
ASSERT_SYS(0, 3, creat("foo", 0600));
ASSERT_EQ(0600, GetMode(3));
ASSERT_SYS(0, 0, fchmod(3, 0400));
ASSERT_EQ(0400, GetMode(3));
ASSERT_SYS(0, 0, fchmod(3, 0600));
ASSERT_EQ(0600, GetMode(3));
ASSERT_SYS(0, 0, close(3));
void __sig_unblock(sigset_t m) {
if (IsWindows() || IsMetal()) {
if (__tls_enabled) {
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
if (_weaken(__sig_check))
_weaken(__sig_check)();
}
} else {
sys_sigprocmask(SIG_SETMASK, &m, 0);
}
}
File renamed without changes.
@@ -32,7 +32,7 @@
.ftrace1
swapcontext:
.ftrace2
#include "libc/calls/getcontext.inc"
#include "libc/intrin/getcontext.inc"
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1 -1
View File
@@ -19,4 +19,4 @@
#include "libc/atomic.h"
#include "libc/calls/internal.h"
atomic_int __umask;
atomic_int __umask = 0777;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+3
View File
@@ -17,5 +17,8 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/runtime/internal.h"
#ifndef __x86_64__
int __pid;
#endif
+21
View File
@@ -91,6 +91,27 @@ $(LIBC_NT_COMDLG32_A).pkg: \
#───────────────────────────────────────────────────────────────────────────────
LIBC_NT_ARTIFACTS += LIBC_NT_SHELL32_A
LIBC_NT_SHELL32 = $(LIBC_NT_SHELL32_A_DEPS) $(LIBC_NT_SHELL32_A)
LIBC_NT_SHELL32_A = o/$(MODE)/libc/nt/shell32.a
LIBC_NT_SHELL32_A_SRCS := $(wildcard libc/nt/shell32/*.S)
LIBC_NT_SHELL32_A_OBJS = $(LIBC_NT_SHELL32_A_SRCS:%.S=o/$(MODE)/%.o)
LIBC_NT_SHELL32_A_CHECKS = $(LIBC_NT_SHELL32_A).pkg
LIBC_NT_SHELL32_A_DIRECTDEPS = LIBC_NT_KERNEL32
LIBC_NT_SHELL32_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_NT_SHELL32_A_DIRECTDEPS),$($(x))))
$(LIBC_NT_SHELL32_A): \
libc/nt/shell32/ \
$(LIBC_NT_SHELL32_A).pkg \
$(LIBC_NT_SHELL32_A_OBJS)
$(LIBC_NT_SHELL32_A).pkg: \
$(LIBC_NT_SHELL32_A_OBJS) \
$(foreach x,$(LIBC_NT_SHELL32_A_DIRECTDEPS),$($(x)_A).pkg)
#───────────────────────────────────────────────────────────────────────────────
LIBC_NT_ARTIFACTS += LIBC_NT_GDI32_A
LIBC_NT_GDI32 = $(LIBC_NT_GDI32_A_DEPS) $(LIBC_NT_GDI32_A)
LIBC_NT_GDI32_A = o/$(MODE)/libc/nt/gdi32.a
+18
View File
@@ -0,0 +1,18 @@
#include "libc/nt/codegen.h"
.imp gdi32,__imp_DescribePixelFormat,DescribePixelFormat
.text.windows
.ftrace1
DescribePixelFormat:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_DescribePixelFormat(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn DescribePixelFormat,globl
.previous
+20
View File
@@ -0,0 +1,20 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_GlobalLock,GlobalLock
.text.windows
.ftrace1
GlobalLock:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov %rdi,%rcx
sub $32,%rsp
call *__imp_GlobalLock(%rip)
leave
#elif defined(__aarch64__)
mov x0,#0
#endif
ret
.endfn GlobalLock,globl
.previous
+20
View File
@@ -0,0 +1,20 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_GlobalUnlock,GlobalUnlock
.text.windows
.ftrace1
GlobalUnlock:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov %rdi,%rcx
sub $32,%rsp
call *__imp_GlobalUnlock(%rip)
leave
#elif defined(__aarch64__)
mov x0,#0
#endif
ret
.endfn GlobalUnlock,globl
.previous
+34
View File
@@ -186,6 +186,8 @@ imp 'GetWindowsDirectoryA' GetWindowsDirectoryA kernel32 2
imp 'GlobalAlloc' GlobalAlloc kernel32 2
imp 'GlobalFree' GlobalFree kernel32 1
imp 'GlobalMemoryStatusEx' GlobalMemoryStatusEx kernel32 1
imp 'GlobalLock' GlobalLock kernel32 1
imp 'GlobalUnlock' GlobalUnlock kernel32 1
imp 'HeapAlloc' HeapAlloc kernel32 3
imp 'HeapCompact' HeapCompact kernel32 2
imp 'HeapCreate' HeapCreate kernel32 3
@@ -376,6 +378,7 @@ imp 'TraceSetInformation' TraceSetInformation advapi32 # Windows 7+
#
# Name Actual DLL Arity
imp 'AdjustWindowRect' AdjustWindowRect user32 3
imp 'AdjustWindowRectEx' AdjustWindowRectEx user32 4
imp 'AnimateWindow' AnimateWindow user32 3
imp 'AppendMenuA' AppendMenuA user32 4
imp 'AppendMenu' AppendMenuW user32 4
@@ -383,6 +386,9 @@ imp 'BeginPaint' BeginPaint user32 2
imp 'BringWindowToTop' BringWindowToTop user32 1
imp 'CallNextHookEx' CallNextHookEx user32 4
imp 'CloseWindow' CloseWindow user32 1
imp 'ClientToScreen' ClientToScreen user32 2
imp 'ClipCursor' ClipCursor user32 1
imp 'CloseClipboard' CloseClipboard user32 0
imp 'CreateIconIndirect' CreateIconIndirect user32 1
imp 'CreateMenu' CreateMenu user32 0
imp 'CreatePopupMenu' CreatePopupMenu user32 0
@@ -395,12 +401,15 @@ imp 'DestroyWindow' DestroyWindow user32 1
imp 'DispatchMessage' DispatchMessageW user32 1
imp 'DrawText' DrawTextW user32 5
imp 'DrawTextEx' DrawTextExW user32 6
imp 'EmptyClipboard' EmptyClipboard user32 0
imp 'EndPaint' EndPaint user32 2
imp 'EnumChildWindows' EnumChildWindows user32 3
imp 'FillRect' FillRect user32 3
imp 'FindWindow' FindWindowW user32 2
imp 'FindWindowEx' FindWindowExW user32 4
imp 'GetAsyncKeyState' GetAsyncKeyState user32 1
imp 'GetClientRect' GetClientRect user32 2
imp 'GetClipboardData' GetClipboardData user32 1
imp 'GetCursor' GetCursor user32 0
imp 'GetCursorPos' GetCursorPos user32 1
imp 'GetDC' GetDC user32 1
@@ -409,9 +418,12 @@ imp 'GetKeyState' GetKeyState user32 1
imp 'GetKeyboardLayout' GetKeyboardLayout user32 1
imp 'GetMenu' GetMenu user32 1
imp 'GetMessage' GetMessageW user32 4
imp 'GetMonitorInfo' GetMonitorInfoW user32 2
imp 'GetRawInputData' GetRawInputData user32 5
imp 'GetParent' GetParent user32 1
imp 'GetShellWindow' GetShellWindow user32 0
imp 'GetSystemMenu' GetSystemMenu user32 2
imp 'GetSystemMetrics' GetSystemMetrics user32 1
imp 'GetWindow' GetWindow user32 2
imp 'GetWindowPlacement' GetWindowPlacement user32 2
imp 'GetWindowRect' GetWindowRect user32 2
@@ -432,13 +444,22 @@ imp 'MapVirtualKeyEx' MapVirtualKeyExW user32 3
imp 'MessageBox' MessageBoxW user32 4
imp 'MessageBoxEx' MessageBoxExW user32 5
imp 'MoveWindow' MoveWindow user32 6
imp 'MonitorFromPoint' MonitorFromPoint user32 2
imp 'MonitorFromWindow' MonitorFromWindow user32 2
imp 'OpenClipboard' OpenClipboard user32 1
imp 'PeekMessage' PeekMessageW user32 5
imp 'PostMessage' PostMessageW user32 4
imp 'PostQuitMessage' PostQuitMessage user32 1
imp 'PtInRect' PtInRect user32 2
imp 'RedrawWindow' RedrawWindow user32 4
imp 'RegisterClass' RegisterClassW user32 1
imp 'RegisterClassEx' RegisterClassExW user32 1
imp 'RegisterRawInputDevices' RegisterRawInputDevices user32 3
imp 'ReleaseCapture' ReleaseCapture user32 0
imp 'ReleaseDC' ReleaseDC user32 2
imp 'ScreenToClient' ScreenToClient user32 2
imp 'SetClipboardData' SetClipboardData user32 2
imp 'SetCursorPos' SetCursorPos user32 2
imp 'SendMessage' SendMessageW user32 4
imp 'SetCapture' SetCapture user32 1
imp 'SetClassLong' SetClassLongW user32 3
@@ -446,6 +467,7 @@ imp 'SetCursor' SetCursor user32 1
imp 'SetParent' SetParent user32 2
imp 'SetTimer' SetTimer user32 4
imp 'SetWindowLong' SetWindowLongW user32 3
imp 'SetWindowLongPtr' SetWindowLongPtrW user32 3
imp 'SetWindowPlacement' SetWindowPlacement user32 2
imp 'SetWindowPos' SetWindowPos user32 7
imp 'SetWindowText' SetWindowTextW user32 2
@@ -454,12 +476,23 @@ imp 'SetWindowsHookEx' SetWindowsHookExW user32 4
imp 'ShowCaret' ShowCaret user32 1
imp 'ShowCursor' ShowCursor user32 1
imp 'ShowWindow' ShowWindow user32 2
imp 'TrackMouseEvent' TrackMouseEvent user32 1
imp 'TrackPopupMenu' TrackPopupMenu user32 7
imp 'TranslateMessage' TranslateMessage user32 1
imp 'UnhookWindowsHook' UnhookWindowsHook user32 2
imp 'UnhookWindowsHookEx' UnhookWindowsHookEx user32 1
imp 'UnregisterClass' UnregisterClassW user32 2
imp 'UpdateWindow' UpdateWindow user32 1
imp 'WaitForInputIdle' WaitForInputIdle user32 2
imp 'WindowFromPoint' WindowFromPoint user32 1
# SHELL32.DLL
#
# Name Actual DLL Arity
imp 'CommandLineToArgv' CommandLineToArgvW shell32 2
imp 'DragAcceptFiles' DragAcceptFiles shell32 2
imp 'DragFinish' DragFinish shell32 1
imp 'DragQueryFile' DragQueryFileW shell32 4
# GDI32.DLL
#
@@ -473,6 +506,7 @@ imp 'CreateDIBSection' CreateDIBSection gdi32 6
imp 'CreateRectRgn' CreateRectRgn gdi32 4
imp 'DeleteDC' DeleteDC gdi32 1
imp 'DeleteObject' DeleteObject gdi32 1
imp 'DescribePixelFormat' DescribePixelFormat gdi32 4
imp 'GetPixel' GetPixel gdi32 3
imp 'RestoreDC' RestoreDC gdi32 2
imp 'SaveDC' SaveDC gdi32 1
+18
View File
@@ -0,0 +1,18 @@
#include "libc/nt/codegen.h"
.imp shell32,__imp_CommandLineToArgvW,CommandLineToArgvW
.text.windows
.ftrace1
CommandLineToArgv:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_CommandLineToArgvW(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn CommandLineToArgv,globl
.previous
+18
View File
@@ -0,0 +1,18 @@
#include "libc/nt/codegen.h"
.imp shell32,__imp_DragAcceptFiles,DragAcceptFiles
.text.windows
.ftrace1
DragAcceptFiles:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_DragAcceptFiles(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn DragAcceptFiles,globl
.previous
+20
View File
@@ -0,0 +1,20 @@
#include "libc/nt/codegen.h"
.imp shell32,__imp_DragFinish,DragFinish
.text.windows
.ftrace1
DragFinish:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov %rdi,%rcx
sub $32,%rsp
call *__imp_DragFinish(%rip)
leave
#elif defined(__aarch64__)
mov x0,#0
#endif
ret
.endfn DragFinish,globl
.previous
+18
View File
@@ -0,0 +1,18 @@
#include "libc/nt/codegen.h"
.imp shell32,__imp_DragQueryFileW,DragQueryFileW
.text.windows
.ftrace1
DragQueryFile:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_DragQueryFileW(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn DragQueryFile,globl
.previous
+18
View File
@@ -0,0 +1,18 @@
#include "libc/nt/codegen.h"
.imp user32,__imp_AdjustWindowRectEx,AdjustWindowRectEx
.text.windows
.ftrace1
AdjustWindowRectEx:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_AdjustWindowRectEx(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn AdjustWindowRectEx,globl
.previous
Loaded 100 of 288 files, more files were not shown because too many files have changed in this diff. Show more