mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-10 17:18:50 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
746660066f | ||
|
|
fd15b2d7a3 | ||
|
|
e228aa3e14 | ||
|
|
9ddbfd921e | ||
|
|
729f7045e3 | ||
|
|
e47d67ba9b | ||
|
|
2477677c85 | ||
|
|
abdf6c9c26 | ||
|
|
5c3f854acb | ||
|
|
ad0a7c67c4 | ||
|
|
1312f60245 | ||
|
|
cafdb456ed | ||
|
|
4e9566cd33 | ||
|
|
5ce5fb6f2a | ||
|
|
d3279d3c0d | ||
|
|
e62ff3e19c | ||
|
|
913b573661 | ||
|
|
9add248c9b | ||
|
|
beb090b83f | ||
|
|
107d335c0d | ||
|
|
bd6630d62d | ||
|
|
a120bc7149 | ||
|
|
baad1df71d | ||
|
|
4e44517c9c | ||
|
|
26663dea9c | ||
|
|
23da0d75a5 | ||
|
|
4b2a00fd4a | ||
|
|
2f4e6e8d77 | ||
|
|
dd249ff5d4 | ||
|
|
4abcba8d8f | ||
|
|
dc1afc968b | ||
|
|
5edc0819c0 | ||
|
|
706cb66310 | ||
|
|
a8bc7ac119 | ||
|
|
d8fac40f55 | ||
|
|
000d6dbb0f | ||
|
|
17a85e4790 | ||
|
|
ad11fc32ad | ||
|
|
dcf9596620 | ||
|
|
85c58be942 | ||
|
|
e4d6eb382a | ||
|
|
fef24d622a | ||
|
|
12cc2de22e | ||
|
|
70603fa6ea | ||
|
|
9255113011 | ||
|
|
333c3d1f0a | ||
|
|
518eabadf5 | ||
|
|
556a294363 | ||
|
|
80804ccfff |
No files matched your search
Vendored
+1
-1
@@ -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",
|
||||
|
||||
@@ -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/*) \
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 $<
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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,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__ */
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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
@@ -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.
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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
@@ -17,5 +17,8 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/internal.h"
|
||||
#ifndef __x86_64__
|
||||
|
||||
int __pid;
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in new issue
Block a user