Compare commits

...
49 Commits
Author SHA1 Message Date
Justine Tunney 8621034d42 Release Cosmopolitan v3.6.2 2024-07-27 20:20:54 -07:00
Justine Tunney 32292aee84 Fix build determinism issue 2024-07-27 19:03:32 -07:00
Justine Tunney 81dd9639ed Fix bug in tool/decode/zip 2024-07-27 18:34:21 -07:00
Justine Tunney f147d3dde9 Fix some static analysis issues 2024-07-27 09:16:54 -07:00
Justine Tunney fb54604b31 Upgrade to superconfigure z0.0.48
Our cosmocc binaries are now built with GCC 14.1, using the Cosmo commit
efb3a34608 from yesterday.

GCC is now configured using --enable-analyzer so you can use -fanalyzer.
2024-07-27 08:35:36 -07:00
Justine Tunney cdfcee51ca Properly serialize fork() operations
This change solves an issue where many threads attempting to spawn forks
at once would cause fork() performance to degrade with the thread count.
Things got real nasty on NetBSD, which slowed down the whole test fleet,
because there's no vfork() and we're forced to use fork() in our server.

   threads      count task
         1       1062 fork+exit+wait
         2        668 fork+exit+wait
         4         66 fork+exit+wait
         8         19 fork+exit+wait
        16         22 fork+exit+wait
        32         16 fork+exit+wait

Things are now much less bad on NetBSD, but not great, since it does not
have futexes; we rely on its semaphore file descriptors to do conditions

   threads      count task
         1       1085 fork+exit+wait
         2        842 fork+exit+wait
         4        532 fork+exit+wait
         8        400 fork+exit+wait
        16        276 fork+exit+wait
        32         66 fork+exit+wait

With OpenBSD which also lacks vfork(), things were just as bad as NetBSD

   threads      count task
         1        584 fork+exit+wait
         2        687 fork+exit+wait
         4        206 fork+exit+wait
         8         24 fork+exit+wait
        16         33 fork+exit+wait
        32         26 fork+exit+wait

But since OpenBSD has futexes fork() works terrifically thanks to *NSYNC

   threads      count task
         1        525 fork+exit+wait
         2        580 fork+exit+wait
         4        451 fork+exit+wait
         8        479 fork+exit+wait
        16        408 fork+exit+wait
        32        373 fork+exit+wait

This issue would most likely only manifest itself, when pthread_atfork()
callers manage to slip a spin lock into the outermost position of fork's
list of locks. Since fork() is very slow, a spin lock can be devastating

Needless to say vfork() rules and anyone who says differently is kidding
themselves. Look at what a FreeBSD 14.1 virtual machine with equal specs
can do over the course of three hundred milliseconds.

   threads      count task
         1       2559 vfork+exit+wait
         2       5389 vfork+exit+wait
         4      34933 vfork+exit+wait
         8      43273 vfork+exit+wait
        16      49648 vfork+exit+wait
        32      40247 vfork+exit+wait

So it's a shame that so few OSes support vfork(). It creates an unsavory
situation, where someone wanting to build a server that spawns processes
would be better served to not use threads and favor a multiprocess model
2024-07-27 08:23:44 -07:00
Justine Tunney 18a620cc1a Make some improvements of little consequence 2024-07-27 08:20:18 -07:00
Justine Tunney 690d3df66e Expand the virtual address space on Windows 2024-07-27 08:19:05 -07:00
Justine Tunney efb3a34608 Fix package.sh build error 2024-07-26 06:57:24 -07:00
Justine Tunney 642e9cb91a Introduce cosmocc flags -mdbg -mtiny -moptlinux
The cosmocc.zip toolchain will now include four builds of the libcosmo.a
runtime libraries. You can pass the -mdbg flag if you want to debug your
cosmopolitan runtime. You can pass the -moptlinux flag if you don't want
windows code lurking in your binary. See tool/cosmocc/README.md for more
details on how these flags may be used and their important implications.
2024-07-26 05:10:25 -07:00
Justine Tunney 59692b0882 Make spinlocks faster (take two)
This change is green on x86 and arm test fleet.
2024-07-26 00:45:24 -07:00
Justine Tunney 02e1cbcd00 Revert "Make spin locks go faster"
This reverts commit c8e25d811c.
2024-07-25 22:24:32 -07:00
Justine Tunney 0679cfeb41 Fix build 2024-07-25 22:12:08 -07:00
Justine Tunney edd5e2c8e3 Expose gethostbyname() 2024-07-25 19:21:35 -07:00
Justine Tunney c8e25d811c Make spin locks go faster 2024-07-25 17:37:11 -07:00
Justine Tunney a31d5ea399 Remove cosmoc++ compiler warning 2024-07-25 15:20:41 -07:00
Justine Tunney 7d88343973 Release Cosmopolitan v3.6.1 2024-07-25 13:34:02 -07:00
Justine Tunney 3915ca0f71 Have cosmocc define -Wno-implicit-int
GCC 13+ changed its policies to be very aggressive about breaking builds
that have anything resembling K&R C. I very strongly disagree with these
decisions. Users who think their compiler should should also be a linter
are perfectly welcome to opt-in to -Wimplicit-int.
2024-07-25 05:51:57 -07:00
Justine Tunney 2c4b88753b Add special errno handling to libcxx 2024-07-25 01:23:02 -07:00
Justine Tunney 0f486a13c8 Fix fdlibm license 2024-07-24 20:42:08 -07:00
Justine Tunney 1020dd41cc bzero() should be defined without special defines 2024-07-24 16:15:30 -07:00
Justine Tunney d3a13e8d70 Improve lock hierarchy
- NetBSD no longer needs a spin lock to create semaphores
- Windows fork() now locks process manager in correct order
2024-07-24 16:05:48 -07:00
Justine Tunney 7ba9a73840 Remove more _Atomic keywords from public headers
It's been thirteen years and C++ still hasn't implemented this wonderful
simple builtin keyword. In C++23 a solution was provided for making this
work in C++ which is libcxx's stdatomic.h. Including that header schleps
in literally 253 unique header files!! Many of the header files it needs
are libc header files like pthread.h where we need to have the _Atomic()
keyword, but since <atomic> depends on pthreads we can't have it include
the <stdatomic.h> header that defines _Atomic for C++ users, and instead
we simply make the type non-atomic, hoping and praying only C code shall
use those internal data structures. This just shows how STL clowns can't
be trusted to define the innermost primitives of a language. They should
instead be focusing on being the best at algorithms and data structures.
2024-07-24 13:56:03 -07:00
Justine Tunney 4a1ae86124 Make some code build faster 2024-07-24 12:26:52 -07:00
Justine Tunney 5dd7ddb9ea Remove bad defines from early days of project
These definitions were causing issues with building LLVM. It is possible
they also caused crashes we've seen with our MacOS ARM64 OpenMP support.
2024-07-24 12:11:21 -07:00
Justine Tunney f25fbbaaeb Use libcxx abi v1 2024-07-24 09:49:48 -07:00
Justine Tunney fbc4b03d4c Restore support for AMD K8 2024-07-24 08:59:29 -07:00
Justine Tunney e398f3887c Make more improvements to threads and mappings
- NetBSD should now have faster synchronization
- POSIX barriers may now be shared across processes
- An edge case with memory map tracking has been fixed
- Grand Central Dispatch is no longer used on MacOS ARM64
- POSIX mutexes in normal mode now use futexes across processes
2024-07-24 01:19:54 -07:00
Justine Tunney 2187d6d2dd Fix MODE=optlinux for GitHub Actions 2024-07-23 04:17:22 -07:00
Justine Tunney 0602ff6bab Fix MODE=optlinux and MODE=tiny builds 2024-07-23 04:04:19 -07:00
Justine Tunney 5660ec4741 Release Cosmopolitan v3.6.0
This release is an atomic upgrade to GCC 14.1.0 with C23 and C++23
2024-07-23 03:28:19 -07:00
Justine Tunney 62ace3623a Release Cosmopolitan v3.5.9 2024-07-22 21:02:40 -07:00
Justine Tunney 6e809ee49b Add unit test for process shared conditions 2024-07-22 18:48:54 -07:00
Justine Tunney 61c36c1dd6 Allow pthread_condattr_setpshared() to set shared 2024-07-22 18:41:45 -07:00
Justine Tunney 0a9a6f86bb Support process shared condition variables 2024-07-22 16:35:29 -07:00
Justine Tunney 3de6632be6 Graduate some clock_gettime() constants to #define
- CLOCK_THREAD_CPUTIME_ID
- CLOCK_PROCESS_CPUTIME_ID

Cosmo now supports the above constants universally across supported OSes
therefore it's now safe to let programs detect their presence w/ #ifdefs
2024-07-22 07:14:35 -07:00
Justine Tunney c83ec5fdd9 Fix more issues 2024-07-22 02:28:41 -07:00
Justine Tunney 23611cd854 Talk more about alignment 2024-07-22 02:11:09 -07:00
Justine Tunney 62a97c919f Fix typos in APE specification
Fixes #1244
2024-07-22 01:41:44 -07:00
Justine Tunney 16d244614e Mention red zone in APE ABI specification 2024-07-21 21:18:12 -07:00
Justine Tunney 41cc053419 Add more content to APE specification 2024-07-21 20:47:24 -07:00
Justine Tunney 5d2d9e9640 Add back missing TlsAlloc() call
Cosmopolitan Libc once called this important function although somewhere
along the way, possibly in a refactoring, it got removed and __tls_alloc
has always been zero ever since.
2024-07-21 20:45:27 -07:00
Justine Tunney e08a4cd99e Release Cosmopolitan v3.5.8 2024-07-21 17:01:33 -07:00
Justine Tunney 7ebaff34c6 Fix ctype.h and wctype.h 2024-07-21 15:54:17 -07:00
Justine Tunney e7be5a5e2b Upgrade to latest superconfigure 2024-07-21 15:30:47 -07:00
Justine Tunney 30afd6ddbb Improve multithreading 2024-07-21 14:40:45 -07:00
Justine Tunney d3167126aa Fix regression with last commit 2024-07-20 16:43:48 -07:00
Justine Tunney d3f87f4c64 Upgrade to cosmocc v3.5.7 2024-07-20 11:21:26 -07:00
Justine Tunney 29ce25c767 Start writing formal specification for APE 2024-07-20 10:04:22 -07:00
1949 changed files with 121861 additions and 272765 deletions

No files matched your search

+21 -19
View File
@@ -101,19 +101,20 @@ XARGS ?= xargs -P4 -rs8000
DOT ?= dot
CLANG = clang
TMPDIR = o/tmp
AR = build/bootstrap/ar
CP = build/bootstrap/cp
RM = build/bootstrap/rm -f
GZIP = build/bootstrap/gzip
ECHO = build/bootstrap/echo
CHMOD = build/bootstrap/chmod
TOUCH = build/bootstrap/touch
PKG = build/bootstrap/package
MKDEPS = build/bootstrap/mkdeps
ZIPOBJ = build/bootstrap/zipobj
ZIPCOPY = build/bootstrap/zipcopy
PECHECK = build/bootstrap/pecheck
FIXUPOBJ = build/bootstrap/fixupobj
AR = $(BOOTSTRAP)/ar.ape
CP = $(BOOTSTRAP)/cp.ape
RM = $(BOOTSTRAP)/rm.ape -f
GZIP = $(BOOTSTRAP)/gzip.ape
ECHO = $(BOOTSTRAP)/echo.ape
CHMOD = $(BOOTSTRAP)/chmod.ape
TOUCH = $(BOOTSTRAP)/touch.ape
PKG = $(BOOTSTRAP)/package.ape
MKDEPS = $(BOOTSTRAP)/mkdeps
ZIPOBJ = $(BOOTSTRAP)/zipobj
ZIPCOPY = $(BOOTSTRAP)/zipcopy
PECHECK = $(BOOTSTRAP)/pecheck
FIXUPOBJ = $(BOOTSTRAP)/fixupobj
OBJBINCOPY = $(BOOTSTRAP)/objbincopy
MKDIR = build/bootstrap/mkdir -p
COMPILE = build/bootstrap/compile -V9 -M2048m -P8192 $(QUOTA)
@@ -149,9 +150,10 @@ export MODE
export SOURCE_DATE_EPOCH
export TMPDIR
COSMOCC = .cosmocc/3.3.5
COSMOCC = .cosmocc/3.6.0
BOOTSTRAP = $(COSMOCC)/bin
TOOLCHAIN = $(COSMOCC)/bin/$(ARCH)-linux-cosmo-
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.3.5 db78fd8d3f8706e9dff4be72bf71d37a3f12062f212f407e1c33bc4af3780dd0)
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.6.0 4918c45ac3e0972ff260e2a249e25716881e39fb679d5e714ae216a2ef6c3f7e)
AS = $(TOOLCHAIN)as
CC = $(TOOLCHAIN)gcc
@@ -291,8 +293,7 @@ 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 third_party/bash/BUILD.mk #─┘
include third_party/regex/BUILD.mk #─┘
include third_party/tidy/BUILD.mk
include third_party/BUILD.mk
include third_party/nsync/testing/BUILD.mk
@@ -434,6 +435,9 @@ loc: o/$(MODE)/tool/build/summy
# PLEASE: MAINTAIN TOPOLOGICAL ORDER
# FROM HIGHEST LEVEL TO LOWEST LEVEL
COSMOPOLITAN_OBJECTS = \
CTL \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_OPENMP \
TOOL_ARGS \
NET_HTTP \
LIBC_SOCK \
@@ -443,7 +447,6 @@ COSMOPOLITAN_OBJECTS = \
THIRD_PARTY_GETOPT \
LIBC_LOG \
THIRD_PARTY_TZ \
THIRD_PARTY_OPENMP \
THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB_GZ \
THIRD_PARTY_LIBCXXABI \
@@ -454,7 +457,6 @@ COSMOPOLITAN_OBJECTS = \
LIBC_THREAD \
LIBC_PROC \
THIRD_PARTY_NSYNC_MEM \
CTL \
LIBC_MEM \
THIRD_PARTY_DLMALLOC \
LIBC_DLOPEN \
+1 -1
View File
@@ -244,7 +244,7 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
| Platform | Min Version | Circa |
| :--- | ---: | ---: |
| AMD | K8 Venus | 2005 |
| AMD | K8 | 2003 |
| Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
+2 -2
View File
@@ -218,10 +218,10 @@ o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=8 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
@$(COMPILE) -AOBJBINCOPY -w build/bootstrap/objbincopy -f -o $@ $<
@$(COMPILE) -AOBJBINCOPY -w $(OBJBINCOPY) -f -o $@ $<
o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.elf.dbg
@$(COMPILE) -AOBJBINCOPY -w build/bootstrap/objbincopy -fm -o $@ $<
@$(COMPILE) -AOBJBINCOPY -w $(OBJBINCOPY) -fm -o $@ $<
APE_LOADER_LDFLAGS = \
-static \
+703
View File
@@ -0,0 +1,703 @@
# Actually Portable Executable Specification v0.1
Actually Portable Executable (APE) is an executable file format that
polyglots the Windows Portable Executable (PE) format with a UNIX Sixth
Edition style shell script that doesn't have a shebang. This makes it
possible to produce a single file binary that executes on the stock
installations of the many OSes and architectures.
## Supported OSes and Architectures
- AMD64
- Linux
- MacOS
- Windows
- FreeBSD
- OpenBSD
- NetBSD
- BIOS
- ARM64
- Linux
- MacOS
- FreeBSD
- Windows (non-native)
## File Header
APE defines three separate file magics, all of which are 8 characters
long. Any file that starts with one of these magic values can be
considered an APE program.
### (1) APE MZ Magic
- ASCII: `MZqFpD='`
- Hex: 4d 5a 71 46 70 44 3d 27
This is the canonical magic used by almost all APE programs. It enables
maximum portability between OSes. When interpreted as a shell script, it
is assigning a single quoted string to an unused variable. The shell
will then ignore subsequent binary content that's placed inside the
string.
It is strongly recommended that this magic value be immediately followed
by a newline (\n or hex 0a) character. Some shells, e.g. FreeBSD SH and
Zsh impose a binary safety check before handing off files that don't
have a shebang to `/bin/sh`. That check applies to the first line, which
can't contain NUL characters.
The letters were carefully chosen so as to be valid x86 instructions in
all operating modes. This makes it possible to store a BIOS bootloader
disk image inside an APE binary. For example, simple CLI programs built
with Cosmopolitan Libc will boot from BIOS into long mode if they're
treated as a floppy disk image.
The letters also allow for the possibility of being treated on x86-64 as
a flat executable, where the PE / ELF / Mach-O executable structures are
ignored, and execution simply begins at the beginning of the file,
similar to how MS-DOS .COM binaries work.
The 0x4a relative offset of the magic causes execution to jump into the
MS-DOS stub defined by Portable Executable. APE binaries built by Cosmo
Libc use tricks in the MS-DOS stub to check the operating mode and then
jump to the appropriate entrypoint, e.g. `_start()`.
#### Decoded as i8086
```asm
dec %bp
pop %dx
jno 0x4a
jo 0x4a
```
#### Decoded as i386
```asm
push %ebp
pop %edx
jno 0x4a
jo 0x4a
```
#### Decoded as x86-64
```asm
rex.WRB
pop %r10
jno 0x4a
jo 0x4a
```
### (2) APE UNIX-Only Magic
- ASCII: `jartsr='`
- Hex: 6a 61 72 74 73 72 3d 27
Being a novel executable format that was first published in 2020, the
APE file format is less understood by industry tools compared to the PE,
ELF, and Mach-O executable file formats, which have been around for
decades. For this reason, APE programs that use the MZ magic above can
attract attention from Windows AV software, which may be unwanted by
developers who aren't interested in targeting the Windows platform.
Therefore the `jartsr='` magic is defined which enables the creation of
APE binaries that can safely target all non-Windows platforms. Even
though this magic is less common, APE interpreters and binfmt-misc
installations MUST support this.
It is strongly recommended that this magic value be immediately followed
by a newline (\n or hex 0a) character. Some shells, e.g. FreeBSD SH and
Zsh impose a binary safety check before handing off files that don't
have a shebang to `/bin/sh`. That check applies to the first line, which
can't contain NUL characters.
The letters were carefully chosen so as to be valid x86 instructions in
all operating modes. This makes it possible to store a BIOS bootloader
disk image inside an APE binary. For example, simple CLI programs built
with Cosmopolitan Libc will boot from BIOS into long mode if they're
treated as a floppy disk image.
The letters also allow for the possibility of being treated on x86-64 as
a flat executable, where the PE / ELF / Mach-O executable structures are
ignored, and execution simply begins at the beginning of the file,
similar to how MS-DOS .COM binaries work.
The 0x78 relative offset of the magic causes execution to jump into the
MS-DOS stub defined by Portable Executable. APE binaries built by Cosmo
Libc use tricks in the MS-DOS stub to check the operating mode and then
jump to the appropriate entrypoint, e.g. `_start()`.
#### Decoded as i8086 / i386 / x86-64
```asm
push $0x61
jb 0x78
jae 0x78
```
### (3) APE Debug Magic
- ASCII: `APEDBG='`
- Hex: 41 50 45 44 42 47 3d 27
While APE files must be valid shell scripts, in practice, UNIX systems
will oftentimes be configured to provide a faster safer alternative to
loading an APE binary through `/bin/sh`. The Linux Kernel can be patched
to have execve() recognize the APE format and directly load its embedded
ELF header. Linux systems can also use binfmt-misc to recognize APE's MZ
and jartsr magic, and pass them to a userspace program named `ape` that
acts as an interpreter. In such environments, the need sometimes arises
to be able to test that the `/bin/sh` is working correctly, in which
case the `APEDBG='` magic is RECOMMENDED.
APE interpreters, execve() implementations, and binfmt-misc installs
MUST ignore this magic. If necessary, steps can be taken to help files
with this magic be passed to `/bin/sh` like a normal shebang-less shell
script for execution.
## Embedded ELF Header
APE binaries MAY embed an ELF header inside them. Unlike conventional
executable file formats, this header is not stored at a fixed offset.
It's instead encoded as octal escape codes in a shell script `printf`
statement. For example:
```
printf '\177ELF\2\1\1\011\0\0\0\0\0\0\0\0\2\0\076\0\1\0\0\0\166\105\100\000\000\000\000\000\060\013\000\000\000\000\000\000\000\000\000\000\000\000\000\000\165\312\1\1\100\0\070\0\005\000\0\0\000\000\000\000'
```
This `printf` statement MUST appear in the first 8192 bytes of the APE
executable, so as to limit how much of the initial portion of a file an
interpreter must load.
Multiple such `printf` statements MAY appear in the first 8192 bytes, in
order to specify multiple architectures. For example, fat binaries built
by the `apelink` program (provided by Cosmo Libc) will have two encoded
ELF headers, for AMD64 and ARM64, each of which point into the proper
file offsets for their respective native code. Therefore, kernels and
interpreters which load the APE format directly MUST check the
`e_machine` field of the `Elf64_Ehdr` that's decoded from the octal
codes, before accepting a `printf` shell statement as valid.
These printf statements MUST always use only unescaped ASCII characters
or octal escape codes. These printf statements MUST NOT use space saving
escape codes such as `\n`. For example, rather than saying `\n` it would
be valid to say `\012` instead. It's also valid to say `\12` but only if
the encoded characters that follow aren't an octal digit.
For example, the following algorithm may be used for parsing octal:
```c
static int ape_parse_octal(const unsigned char page[8192], int i, int *pc)
{
int c;
if ('0' <= page[i] && page[i] <= '7') {
c = page[i++] - '0';
if ('0' <= page[i] && page[i] <= '7') {
c *= 8;
c += page[i++] - '0';
if ('0' <= page[i] && page[i] <= '7') {
c *= 8;
c += page[i++] - '0';
}
}
*pc = c;
}
return i;
}
```
APE aware interpreters SHOULD only take `e_machine` into consideration.
It is the responsibility of the `_start()` function to detect the OS.
Therefore, multiple `printf` statements are only embedded in the shell
script for different CPU architectures.
The OS ABI field of an APE embedded `Elf64_Ehdr` SHOULD be set to
`ELFOSABI_FREEBSD`, since it's the only UNIX OS APE supports that
actually checks the field. However different values MAY be chosen for
binaries that don't intend to have FreeBSD in their support vector.
Counter-intuitively, the ARM64 ELF header is used on the MacOS ARM64
platform when loading from fat binaries.
## Embedded Mach-O Header (x86-64 only)
APE shell scripts that support MacOS on AMD64 must use the `dd` command
in a very specific way to specify how the embedded binary Macho-O header
is copied backward to the start of the file. For example:
```
dd if="$o" of="$o" bs=8 skip=433 count=66 conv=notrunc
```
These `dd` statements have traditionally been generated by the GNU as
and ld.bfd programs by encoding ASCII into 64-bit linker relocations,
which necessitated a fixed width for integer values. It took several
iterations over APE's history before we eventually got it right:
- `arg=" 9293"` is how we originally had ape do it
- `arg=$(( 9293))` b/c busybox sh disliked quoted space
- `arg=9293 ` is generated by modern apelink program
Software that parses the APE file format, which needs to extract the
Macho-O x86-64 header SHOULD support the old binaries that use the
previous encodings. To make backwards compatibility simple the following
regular expression may be used, which generalizes to all defined
formats:
```c
regcomp(&rx,
"bs=" // dd block size arg
"(['\"] *)?" // #1 optional quote w/ space
"(\\$\\(\\( *)?" // #2 optional math w/ space
"([[:digit:]]+)" // #3
"( *\\)\\))?" // #4 optional math w/ space
"( *['\"])?" // #5 optional quote w/ space
" +" //
"skip=" // dd skip arg
"(['\"] *)?" // #6 optional quote w/ space
"(\\$\\(\\( *)?" // #7 optional math w/ space
"([[:digit:]]+)" // #8
"( *\\)\\))?" // #9 optional math w/ space
"( *['\"])?" // #10 optional quote w/ space
" +" //
"count=" // dd count arg
"(['\"] *)?" // #11 optional quote w/ space
"(\\$\\(\\( *)?" // #12 optional math w/ space
"([[:digit:]]+)", // #13
REG_EXTENDED);
```
For further details, see the canonical implementation in
`cosmopolitan/tool/build/assimilate.c`.
## Static Linking
Actually Portable Executables are always statically linked. This
revision of the specification does not define any facility for storing
code in dynamic shared objects.
Cosmopolitan Libc provides a solution that enables APE binaries have
limited access to dlopen(). By manually loading a platform-specific
executable and asking the OS-specific libc's dlopen() to load
OS-specific libraries, it becomes possible to use GPUs and GUIs. This
has worked great for AI projects like llamafile.
There is no way for an Actually Portable Executable to interact with
OS-specific dynamic shared object extension modules to programming
languages. For example, a Lua interpreter compiled as an Actually
Portable Executable would have no way of linking extension libraries
downloaded from the Lua Rocks package manager. This is primarily because
different OSes define incompatible ABIs.
While it was possible to polyglot PE+ELF+MachO to create multi-OS
executables, it simply isn't possible to do that same thing for
DLL+DYLIB+SO. Therefore, in order to have DSOs, APE would need to either
choose one of the existing formats or invent one of its own, and then
develop its own parallel ecosystem of extension software. In the future,
the APE specification may expand to encompass this. However the focus to
date has been exclusively on executables with limited dlopen() support.
## Application Binary Interface (ABI)
APE binaries use the System V ABI, as defined by:
- [System V ABI - AMD64 Architecture Processor Supplement](https://gitlab.com/x86-psABIs/x86-64-ABI)
- AARCH64 has a uniform consensus defined by ARM Limited
There are however a few changes we've had to make.
### No Red Zone
Actually Portable Executables that have Windows and/or bare metal in
their support vector MUST be compiled using `-mno-red-zone`. This is
because, on Windows, DLLs and other software lurking in the va-space
might use tricks like SetThreadContext() to take control of a thread
whereas on bare metal, it's also generally accepted that kernel-mode
code cannot assume a red zone either due to hardware interrupts that
pull the exact same kinds of stunts.
APE software that only has truly System V ABI conformant OSes (e.g.
Linux) in their support vector MAY use the red zone optimization.
### Thread Local Storage
#### aarch64
Here's the TLS memory layout on aarch64:
```
x28
%tpidr_el0
│
│ _Thread_local
┌───┼───┬──────────┬──────────┐
│tib│dtv│ .tdata │ .tbss │
├───┴───┴──────────┴──────────┘
│
__get_tls()
```
The ARM64 code in actually portable executables use the `x28` register
to store the address of the thread information block. All aarch64 code
linked into these executables SHOULD be compiled with `-ffixed-x28`
which is supported by both Clang and GCC.
The runtime library for an actually portable executables MAY choose to
use `tpidr_el0` instead, if OSes like MacOS aren't being targeted. For
example, if the goal is to create a Linux-only fat binary linker program
for Musl Libc, then choosing to use the existing `tpidr_el0` convention
would be friction-free alternative.
It's not possible for an APE runtime that targets the full range of OSes
to use the `tpidr_el0` register for TLS because Apple won't allow it. On
MacOS ARM64 systems, this register can only be used by a runtime to
implement the `sched_getcpu()` system call. It's reserved by MacOS.
#### x86-64
Here's the TLS memory layout on x86_64:
```
__get_tls()
│
%fs OpenBSD/NetBSD
_Thread_local │
┌───┬──────────┬──────────┼───┐
│pad│ .tdata │ .tbss │tib│
└───┴──────────┴──────────┼───┘
│
Linux/FreeBSD/Windows/Mac %gs
```
Quite possibly the greatest challenge in Actually Portable Executable
working, has been overcoming the incompatibilities between OSes in how
thread-local storage works on x86-64. The AMD64 architecture defines two
special segment registers. Every OS uses one of these segment registers
to implement TLS support. However not all OSes agree on which register
to use. Some OSes grant userspace the power to define either of these
registers to hold any value that is desired. Some OSes only effectively
allow a single one of them to be changed. Lastly, some OSes, e.g.
Windows, claim ownership of the memory layout these registers point
towards too.
Here's a breakdown on how much power is granted to userspace runtimes by
each OS when it comes to changing amd64 segment registers.
| | %fs | %gs |
|---------|--------------|--------------|
| Linux | unrestricted | unrestricted |
| MacOS | inaccessible | unrestricted |
| Windows | inaccessible | restricted |
| FreeBSD | unrestricted | unrestricted |
| NetBSD | unrestricted | broken |
| OpenBSD | unrestricted | inaccessible |
Therefore, regardless of which register one we choose, some OSes are
going to be incompatible.
APE binaries are always built with a Linux compiler. So another issue
arises in the fact that our Linux-flavored GCC and Clang toolchains
(which are used to produce cross-OS binaries) are also only capable of
producing TLS instructions that use the %fs convention.
To solve these challenges, the `cosmocc` compiler will rewrite binary
objects after they've been compiled by GCC, so that the `%gs` register
is used, rather than `%fs`. Morphing x86-64 binaries after they've been
compiled is normally difficult, due to the complexity of the machine
instruction language. However GCC provides `-mno-tls-direct-seg-refs`
which greatly reduces the complexity of this task. This flag forgoes
some optimizations to make the generated code simpler. Rather than doing
clever arithmetic with `%fs` prefixes, the compiler will always generate
the thread information block address load as a separate instruction.
```c
// Change AMD code to use %gs:0x30 instead of %fs:0
// We assume -mno-tls-direct-seg-refs has been used
static void ChangeTlsFsToGs(unsigned char *p, size_t n) {
unsigned char *e = p + n - 9;
while (p <= e) {
// we're checking for the following expression:
// 0144 == p[0] && // %fs
// 0110 == (p[1] & 0373) && // rex.w (and ignore rex.r)
// (0213 == p[2] || // mov reg/mem → reg (word-sized)
// 0003 == p[2]) && // add reg/mem → reg (word-sized)
// 0004 == (p[3] & 0307) && // mod/rm (4,reg,0) means sib → reg
// 0045 == p[4] && // sib (5,4,0) → (rbp,rsp,0) → disp32
// 0000 == p[5] && // displacement (von Neumann endian)
// 0000 == p[6] && // displacement
// 0000 == p[7] && // displacement
// 0000 == p[8] // displacement
uint64_t w = READ64LE(p) & READ64LE("\377\373\377\307\377\377\377\377");
if ((w == READ64LE("\144\110\213\004\045\000\000\000") ||
w == READ64LE("\144\110\003\004\045\000\000\000")) &&
!p[8]) {
p[0] = 0145; // change %fs to %gs
p[5] = 0x30; // change 0 to 0x30
p += 9;
} else {
++p;
}
}
}
```
By favoring `%gs` we've now ensured friction-free compatibility for the
APE runtime on MacOS, Linux, and FreeBSD which are all able to conform
easily to this convention. However additional work needs to be done at
runtime when an APE program is started on Windows, OpenBSD, and NetBSD.
On these platforms, all executable pages must be faulted and morphed to
fixup the TLS instructions.
On OpenBSD and NetBSD, this is as simple as undoing the example
operation above. Earlier at compile-time we turned `%fs` into `%gs`.
Now, at runtime, `%gs` must be turned back into `%fs`. Since the
executable is morphing itself, this is easier said than done.
OpenBSD for example enforces a `W^X` invariant. Code that's executing
can't modify itself at the same time. The way Cosmopolitan solves this
is by defining a special part of the binary called `.text.privileged`.
This section is aligned to page boundaries. A GNU ld linker script is
used to ensure that code which morphs code is placed into this section,
through the use of a header-defined cosmo-specific keyword `privileged`.
Additionally, the `fixupobj` program is used by the Cosmo build system
to ensure that compiled objects don't contain privileged functions that
call non-privileged functions. Needless to say, `mprotect()` needs to be
a privileged function, so that it can be used to disable the execute bit
on all other parts of the executable except for the privileged section,
thereby making it writable. Once this has been done, code can change.
On Windows the displacement bytes of the TLS instruction are changed to
use the `%gs:0x1480+i*8` ABI where `i` is a number assigned by the WIN32
`TlsAlloc()` API. This avoids the need to call `TlsGetValue()` which is
implemented this exact same way under the hood. Even though 0x1480 isn't
explicitly documented by MSDN, this ABI is believed to be stable because
MSVC generates binaries that use this offset directly. The only caveat
is that `TlsAlloc()` must be called as early in the runtime init as
possible, to ensure an index less than 64 is returned.
### Thread Information Block (TIB)
The Actually Portable Executable Thread Information Block (TIB) is
defined by this version of the specification as follows:
- The 64-bit TIB self-pointer is stored at offset 0x00.
- The 64-bit TIB self-pointer is also stored at offset 0x30.
- The 32-bit `errno` value is stored at offset 0x3c.
All other parts of the thread information block should be considered
unspecified and therefore reserved for future specifications.
The APE thread information block is aligned on a 64-byte boundary.
Cosmopolitan Libc v3.5.8 (c. 2024-07-21) currently implements a thread
information block that's 512 bytes in size.
### Foreign Function Calls
Even though APE programs always use the System V ABI, there arises the
occasional need to interface with foreign functions, e.g. WIN32. The
`__attribute__((__ms_abi__))` annotation introduced by GCC v6 is used
for this purpose.
The ability to change a function's ABI on a case-by-case basis is
surprisingly enough supported by GCC, Clang, NVCC, and even the AMD HIP
compilers for both UNIX systems and Windows. All of these compilers
support both the System V ABI and the Microsoft x64 ABI.
APE binaries will actually favor the Microsoft ABI even when running on
UNIX OSes for certain dlopen() use-cases. For example, if we control the
code to a CUDA module, which we compile on each OS separately from our
main APE binary, then any function that's inside the APE binary whose
pointer may be passed into a foreign module SHOULD be compiled to use
the Microsoft ABI. This is because in practice the OS-specific module
may need to be compiled by MSVC, where MS ABI is the *only* ABI, which
forces our UNIX programs to partially conform. Thankfully, all UNIX
compilers support doing it on a case-by-case basis.
### Char Signedness
Actually Portable Executable defines `char` as signed.
Therefore conformant APE software MUST use `-fsigned-char` when building
code for aarch64, as well as any other architecture that (unlike x86-64)
would otherwise define `char` as being `unsigned char` by default.
This decision was one of the cases where it made sense to offer a more
consistent runtime experience for fat multi-arch binaries. However you
SHOULD still write code to assume `char` can go either way. But if all
you care about is using APE, then you CAN assume `char` is signed.
### Long Double
On AMD64 platforms, APE binaries define `long double` as 80-bit.
On ARM64 platforms, APE binaries define `long double` as 128-bit.
We accept inconsistency in this case, because hardware acceleration is
far more valuable than stylistic consistency in the case of mathematics.
One challenge arises on AMD64 for supporting `long double` across OSes.
Unlike UNIX systems, the Windows Executive on x86-64 initializes the x87
FPU to have double (64-bit) precision rather than 80-bit. That's because
code compiled by MSVC treats `long double` as though it were `double` to
prefer always using the more modern SSE instructions. However System V
requires genuine 80-bit `long double` support on AMD64.
Therefore, if an APE program detects that it's been started on a Windows
x86-64 system, then it SHOULD use the following assembly to initialize
the x87 FPU in System V ABI mode.
```asm
fldcw 1f(%rip)
.rodata
.balign 2
// 8087 FPU Control Word
// IM: Invalid Operation ───────────────┐
// DM: Denormal Operand ───────────────┐│
// ZM: Zero Divide ───────────────────┐││
// OM: Overflow ─────────────────────┐│││
// UM: Underflow ───────────────────┐││││
// PM: Precision ──────────────────┐│││││
// PC: Precision Control ───────┐ ││││││
// {float,∅,double,long double}│ ││││││
// RC: Rounding Control ──────┐ │ ││││││
// {even, →-∞, →+∞, →0} │┌┤ ││││││
// ┌┤││ ││││││
// d││││rr││││││
1: .short 0b00000000000000000001101111111
.previous
```
## Executable File Alignment
Actually Portable Executable is a statically-linked flat executable file
format that is, as a thing in itself, agnostic to file alignments. For
example, the shell script payload at the beginning of the file and its
statements have no such requirements. Alignment requirements are however
imposed by the executable formats that APE wraps.
1. ELF requires that file offsets be congruent with virtual addresses
modulo the CPU page size. So when we add a shell script to the start
of an executable, we need to round up to the page size in order to
maintain ELF's invariant. Although no such roundup is required on the
program segments once the invariant is restored. ELF loaders will
happily map program headers from arbitrary file intervals (which may
overlap) onto arbitrarily virtual intervals (which don't need to be
contiguous). In order to do that, the loaders will generally use
UNIX's mmap() function which is more restrictive and only accepts
addresses and offsets that are page aligned. To make it possible to
map an unaligned ELF program header that could potentially start and
stop at any byte, ELF loaders round-out the intervals, which means
adjacent unrelated data might also get mapped, which may need to be
explicitly zero'd. Thanks to the cleverness of ELF, it's possible to
have an executable file be very tiny, without needing any alignment
bytes, and it'll be loaded into a properly aligned virtual space
where segments can be as sparse as we want them to be.
2. PE doesn't care about congruence and instead defines two separate
kinds of alignment. First, PE requires that the layout of segment
memory inside the file be aligned on at minimum the classic 512 byte
MS-DOS page size. This means that, unlike ELF, some alignment padding
may need to be encoded into the file, making it slightly larger. Next
PE imposes an alignment restriction on segments once they've been
mapped into the virtual address space, which must be rounded to the
system page size. Like ELF, PE segments need to be properly ordered
but they're allowed to drift apart once mapped in a non-contiguous
sparsely mapped way. When inserting shell script content at the start
of a PE file, the most problematic thing is the need to round up to
the 64kb system granularity, which results in a lot of needless bytes
of padding being inserted by a naive second-pass linker.
3. Apple's Mach-O format is the strictest of them all. While both ELF
and PE are defined in such a way that invites great creativity, XNU
will simply refuse to an executable that does anything creative with
alignment. All loaded segments need to both start and end on a page
aligned address. XNU also wants segments to be contiguous similar to
portable executable, except it applies to both the file and virtual
spaces, which must follow the same structure.
Actually Portable Executables must conform to the strictest requirements
demanded by the support vector. Therefore an APE binary that has headers
for all three of the above executable formats MUST conform to the Apple
way of doing things. GNU ld linker scripts aren't very good at producing
ELF binaries that rigidly conform to this simple naive layout. There are
so many ways things can go wrong, where third party code might slip its
own custom section name in-between the linker script sections that are
explicitly defined, thereby causing ELF's powerful features to manifest
and the resulting content overlapping. The best `ld` flag that helps is
`--orphan-handling=error` which can help with explaining such mysteries.
While Cosmopolitan was originally defined to just use stock GNU tools,
this proved intractable over time, and the project has been evolving in
the direction of building its own. Inventing the `apelink` program was
what enabled the project to achieve multi-architecture binaries whereas
previously it was only possible to do multi-OS binaries. In the future,
our hope is that a fast power linker like Mold can be adapted to produce
fat APE binaries directly from object files in one pass.
## Position Independent Code
APE doesn't currently support position independent executable formats.
This is because APE was originally written for the GNU linker, where PIC
and PIE were after-thoughts and never fully incorporated with the older
more powerful linker script techniques upon which APE relies. Future
iterations of this specification are intended to converge on modern
standards, as our tooling becomes developed enough to support it.
However this only applies to the wrapped executable formats themselves.
While our convention to date has been to always load ELF programs at the
4mb mark, this is not guaranteed across OSes and architectures. Programs
should have no expectations that a program will be loaded to any given
address. For example, Cosmo currently implements APE on AARCH64 as
loading executables to a starting address of 0x000800000000. This
address occupies a sweet spot of requirements.
## Address Space
In order to create a single binary that supports as many platforms as
possible without needing to be recompiled, there's a very narrow range
of addresses that can be used. That range is somewhere between 32 bits
and 39 bits.
- Embedded devices that claim to be 64-bit will oftentimes only support
a virtual address space that's 39 bits in size.
- We can't load executable images on AARCH64 beneath 0x100000000 (4gb)
because Apple forbids doing that, possibly in an effort to enforce a
best practice for spotting 32-bit to 64-bit transition bugs. Please
note that this restriction only applies to Apple ARM64 systems. The
x86-64 version of XNU will happily load APE binaries to 0x00400000.
- The AMD64 architecture on desktops and servers can usually be counted
upon to provide a 47-bit address space. The Linux Kernel for instance
grants each userspace program full dominion over addresses 0x00200000
through 0x00007fffffffffff provided the hardware supports this. On
modern workstations supporting Intel and AMD's new PML5T feature which
virtualizes memory using a radix trie that's five layers deep, Linux
is able to offer userspace its choice of fixed addresses from
0x00200000 through 0x00ffffffffffffff. The only exception to this rule
we've encountered so far is that Windows 7 and Windows Vista behaved
similar to embedded devices in reducing the number of va bits.
## Page Size
APE software MUST be page size agnostic. For many years the industry had
converged on a strong consensus of having a page size that's 4096 bytes.
However this convention was never guaranteed. New computers have become
extremely popular, such as Apple Silicon, that use a 16kb page size.
By convention, Cosmopolitan Libc currently generates ELF headers for
x86-64 that are strictly aligned on a 4096-byte page size. On ARM64
Cosmopolitan is currently implemented to always generate ELF headers
aligned on a 16kb page size.
In addition to being page size agnostic, APE software that cares about
working correctly on Windows needs to be aware of the concept of
allocation granularity. While the page size on Windows is generally 4kb
in size, memory mappings can only be created on addresses that aligned
to the system allocation granularity, which is generally 64kb. If you
use a function like mmap() with Cosmopolitan Libc, then the `addr` and
`offset` parameters need to be aligned to `sysconf(_SC_GRANSIZE)` or
else your software won't work on Windows. Windows has other limitations
too, such as lacking the ability to carve or punch holes in mappings.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+36 -26
View File
@@ -14,7 +14,6 @@ ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CCFLAGS += -O2 $(BACKTRACES)
CONFIG_CPPFLAGS += -DSYSDEBUG
TARGET_ARCH ?= -msse3
endif
ifeq ($(MODE), x86_64)
ENABLE_FTRACE = 1
@@ -64,7 +63,6 @@ ENABLE_FTRACE = 1
CONFIG_CCFLAGS += $(BACKTRACES) -O
CONFIG_CPPFLAGS += -DSYSDEBUG -DDWARFLESS
CONFIG_LDFLAGS += -S
TARGET_ARCH ?= -msse3
endif
# Optimized Mode
@@ -84,7 +82,7 @@ ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG
CONFIG_CCFLAGS += $(BACKTRACES) -O3 -fmerge-all-constants
TARGET_ARCH ?= -march=native
CONFIG_TARGET_ARCH ?= -march=native
endif
# Optimized Linux Mode
@@ -102,7 +100,20 @@ CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
TARGET_ARCH ?= -march=native
CONFIG_TARGET_ARCH ?= -march=native
endif
ifeq ($(MODE), x86_64-optlinux)
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
CONFIG_TARGET_ARCH ?= -march=native
endif
ifeq ($(MODE), aarch64-optlinux)
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
endif
# Release Mode
@@ -123,7 +134,6 @@ endif
ifeq ($(MODE), rel)
CONFIG_CPPFLAGS += -DNDEBUG -DDWARFLESS
CONFIG_CCFLAGS += $(BACKTRACES) -O2
TARGET_ARCH ?= -msse3
PYFLAGS += -O1
endif
@@ -139,18 +149,32 @@ endif
ifeq ($(MODE), dbg)
ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O0 -fno-inline
OVERRIDE_CFLAGS += -O0
OVERRIDE_CXXFLAGS += -O0
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined
OVERRIDE_CCFLAGS += -fno-pie
QUOTA ?= -C64 -L300
endif
ifeq ($(MODE), x86_64-dbg)
ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
OVERRIDE_CFLAGS += -O0
OVERRIDE_CXXFLAGS += -O0
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined
TARGET_ARCH ?= -msse3
OVERRIDE_CCFLAGS += -fno-pie
QUOTA ?= -C64 -L300
endif
ifeq ($(MODE), aarch64-dbg)
ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O0 -fno-inline -fdce
OVERRIDE_CFLAGS += -O0 -fdce
OVERRIDE_CXXFLAGS += -O0 -fdce
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined
QUOTA ?= -C64 -L300
endif
@@ -170,7 +194,6 @@ ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CCFLAGS += $(BACKTRACES) -O2
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
TARGET_ARCH ?= -msse3
endif
# Tiny Mode
@@ -200,8 +223,6 @@ CONFIG_CCFLAGS += \
-momit-leaf-frame-pointer \
-foptimize-sibling-calls \
-DDWARFLESS
TARGET_ARCH ?= \
-msse3
PYFLAGS += \
-O2 \
-B
@@ -221,8 +242,6 @@ CONFIG_CCFLAGS += \
-momit-leaf-frame-pointer \
-foptimize-sibling-calls \
-DDWARFLESS
TARGET_ARCH ?= \
-msse3
PYFLAGS += \
-O2 \
-B
@@ -274,8 +293,6 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \
-fno-align-labels \
-fno-align-loops
TARGET_ARCH ?= \
-msse3
endif
# Linux+BSD Tiny Mode
@@ -305,8 +322,6 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \
-fno-align-labels \
-fno-align-loops
TARGET_ARCH ?= \
-msse3
endif
# Unix Tiny Mode
@@ -335,8 +350,6 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \
-fno-align-labels \
-fno-align-loops
TARGET_ARCH ?= \
-msse3
endif
# Tiny Metallic Unix Mode
@@ -365,8 +378,6 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \
-fno-align-labels \
-fno-align-loops
TARGET_ARCH ?= \
-msse3
endif
# no x87 instructions mode
@@ -388,7 +399,6 @@ ENABLE_FTRACE = 1
CONFIG_COPTS += -mlong-double-64
CONFIG_CCFLAGS += $(BACKTRACES) -O2
CONFIG_CPPFLAGS += -DSYSDEBUG -DNOX87
TARGET_ARCH ?= -msse3
endif
# LLVM Mode
@@ -401,7 +411,6 @@ endif
#
ifeq ($(MODE), llvm)
.STRICT = 0
TARGET_ARCH ?= -msse3
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O2
AS = clang
CC = clang
@@ -444,7 +453,6 @@ ifeq ($(MODE), ansi)
CONFIG_CFLAGS += -std=c11
#CONFIG_CPPFLAGS += -ansi
CONFIG_CXXFLAGS += -std=c++11
TARGET_ARCH ?= -msse3
endif
ifneq ($(ENABLE_FTRACE),)
@@ -502,3 +510,5 @@ ifeq ($(ARCH), aarch64)
CONFIG_CCFLAGS += -fpatchable-function-entry=7,6
endif
endif
TARGET_ARCH ?= $(CONFIG_TARGET_ARCH)
+2 -2
View File
@@ -139,10 +139,10 @@ DEFAULT_CPPFLAGS += \
-isystem libc/isystem
DEFAULT_CFLAGS = \
-std=gnu2x
-std=gnu23
DEFAULT_CXXFLAGS = \
-std=gnu++20 \
-std=gnu++23 \
-fno-rtti \
-fno-exceptions \
-fuse-cxa-atexit \
+8 -8
View File
@@ -6,14 +6,14 @@ if [ -n "$OBJDUMP" ]; then
fi
find_objdump() {
if [ -x .cosmocc/3.3.5/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.3.5/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.3.5/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.3.5/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.3.5/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.3.5/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.3.5/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.3.5/bin/$1-linux-musl-objdump"
if [ -x .cosmocc/3.6.0/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.6.0/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.6.0/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.6.0/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.6.0/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.6.0/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.6.0/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.6.0/bin/$1-linux-musl-objdump"
else
echo "error: toolchain not found (try running 'cosmocc --update' or 'make' in the cosmo monorepo)" >&2
exit 1
+1 -1
View File
@@ -31,7 +31,7 @@ if [ ! -f /proc/sys/fs/binfmt_misc/status ]; then
exit 0
fi
if ! build/bootstrap/echo.com -n; then
if ! build/bootstrap/echo -n; then
cat <<'EOF' >&2
ERROR
+1
View File
@@ -17,6 +17,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "istream.h"
#include "libc/ctype.h"
#include "libc/fmt/conv.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
+1
View File
@@ -17,6 +17,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "istringstream.h"
#include "libc/ctype.h"
#include "libc/fmt/conv.h"
#include "libc/str/str.h"
+3
View File
@@ -21,6 +21,9 @@
#include <__atomic/fence.h>
#include <stdckdint.h>
#include "libc/mem/mem.h"
#include "libc/str/str.h"
namespace ctl {
void
+6 -7
View File
@@ -9,14 +9,13 @@ int mulaw(int);
int unmulaw(int);
void *double2byte(long, const void *, double, double) vallocesque;
void *byte2double(long, const void *, double, double) vallocesque;
void *dct(float[restrict hasatleast 8][8], unsigned, float, float, float, float,
float);
void *dctjpeg(float[restrict hasatleast 8][8], unsigned);
void *dct(float[hasatleast 8][8], unsigned, float, float, float, float, float);
void *dctjpeg(float[hasatleast 8][8], unsigned);
double det3(const double[3][3]) nosideeffect;
void *inv3(double[restrict 3][3], const double[restrict 3][3], double);
void *matmul3(double[restrict 3][3], const double[3][3], const double[3][3]);
void *vmatmul3(double[restrict 3], const double[3], const double[3][3]);
void *matvmul3(double[restrict 3], const double[3][3], const double[3]);
void *inv3(double[3][3], const double[3][3], double);
void *matmul3(double[3][3], const double[3][3], const double[3][3]);
void *vmatmul3(double[3], const double[3], const double[3][3]);
void *matvmul3(double[3], const double[3][3], const double[3]);
double rgb2stdtv(double) pureconst;
double rgb2lintv(double) pureconst;
double rgb2stdpc(double, double) pureconst;
+3 -3
View File
@@ -65,8 +65,8 @@
*
* @cost ~100ns
*/
void *dct(float M[restrict hasatleast 8][8], unsigned stride, float c0,
float c1, float c2, float c3, float c4) {
void *dct(float M[hasatleast 8][8], unsigned stride, float c0, float c1,
float c2, float c3, float c4) {
unsigned y, x;
for (y = 0; y < stride * 8; y += stride) {
DCT(M[y][0], M[y][1], M[y][2], M[y][3], M[y][4], M[y][5], M[y][6], M[y][7],
@@ -79,7 +79,7 @@ void *dct(float M[restrict hasatleast 8][8], unsigned stride, float c0,
return M;
}
void *dctjpeg(float M[restrict hasatleast 8][8], unsigned stride) {
void *dctjpeg(float M[hasatleast 8][8], unsigned stride) {
return dct(M, stride, .707106781f, .382683433f, .541196100f, 1.306562965f,
.707106781f);
}
+1 -1
View File
@@ -30,7 +30,7 @@
* @define 𝐀⁻¹=𝐁 such that 𝐀×𝐁=𝐁×𝐀=𝐈ₙ
* @see det3()
*/
void *inv3(double B[restrict 3][3], const double A[restrict 3][3], double d) {
void *inv3(double B[3][3], const double A[3][3], double d) {
d = d ? 1 / d : NAN;
B[0][0] = (A[1][1] * A[2][2] - A[2][1] * A[1][2]) * d;
B[0][1] = (A[2][1] * A[0][2] - A[0][1] * A[2][2]) * d;
+1 -2
View File
@@ -22,8 +22,7 @@
/**
* Multiplies 3×3 matrices.
*/
void *matmul3(double R[restrict 3][3], const double A[3][3],
const double B[3][3]) {
void *matmul3(double R[3][3], const double A[3][3], const double B[3][3]) {
int i, j, k;
memset(R, 0, sizeof(double) * 3 * 3);
for (i = 0; i < 3; ++i) {
+1 -1
View File
@@ -23,7 +23,7 @@
*
* @see vmatmul3() for noncommutative corollary
*/
void *matvmul3(double R[restrict 3], const double M[3][3], const double V[3]) {
void *matvmul3(double R[3], const double M[3][3], const double V[3]) {
R[0] = V[0] * M[0][0] + V[1] * M[0][1] + V[2] * M[0][2];
R[1] = V[0] * M[1][0] + V[1] * M[1][1] + V[2] * M[1][2];
R[2] = V[0] * M[2][0] + V[1] * M[2][1] + V[2] * M[2][2];
+1 -1
View File
@@ -23,7 +23,7 @@
*
* @see matvmul3() for noncommutative corollary
*/
void *vmatmul3(double R[restrict 3], const double V[3], const double M[3][3]) {
void *vmatmul3(double R[3], const double V[3], const double M[3][3]) {
R[0] = V[0] * M[0][0] + V[1] * M[1][0] + V[2] * M[2][0];
R[1] = V[0] * M[0][1] + V[1] * M[1][1] + V[2] * M[2][1];
R[2] = V[0] * M[0][2] + V[1] * M[1][2] + V[2] * M[2][2];
+8 -9
View File
@@ -31,9 +31,8 @@
#include "dsp/mpeg/video.h"
#include "libc/log/check.h"
forceinline void plm_video_process_macroblock(plm_video_t *self,
uint8_t *restrict d,
uint8_t *restrict s, int motion_h,
forceinline void plm_video_process_macroblock(plm_video_t *self, uint8_t *d,
uint8_t *s, int motion_h,
int motion_v, bool interpolate,
unsigned BW) {
unsigned si, di, max_address;
@@ -155,17 +154,17 @@ forceinline void plm_video_process_macroblock(plm_video_t *self,
}
}
void plm_video_process_macroblock_8(plm_video_t *self, uint8_t *restrict d,
uint8_t *restrict s, int motion_h,
int motion_v, bool interpolate) {
void plm_video_process_macroblock_8(plm_video_t *self, uint8_t *d, uint8_t *s,
int motion_h, int motion_v,
bool interpolate) {
DCHECK_ALIGNED(8, d);
DCHECK_ALIGNED(8, s);
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 8);
}
void plm_video_process_macroblock_16(plm_video_t *self, uint8_t *restrict d,
uint8_t *restrict s, int motion_h,
int motion_v, bool interpolate) {
void plm_video_process_macroblock_16(plm_video_t *self, uint8_t *d, uint8_t *s,
int motion_h, int motion_v,
bool interpolate) {
DCHECK_ALIGNED(16, d);
DCHECK_ALIGNED(16, s);
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 16);
+4 -4
View File
@@ -51,10 +51,10 @@ typedef struct plm_video_t {
int assume_no_b_frames;
} plm_video_t;
void plm_video_process_macroblock_8(plm_video_t *, uint8_t *restrict,
uint8_t *restrict, int, int, bool);
void plm_video_process_macroblock_16(plm_video_t *, uint8_t *restrict,
uint8_t *restrict, int, int, bool);
void plm_video_process_macroblock_8(plm_video_t *, uint8_t *, uint8_t *, int,
int, bool);
void plm_video_process_macroblock_16(plm_video_t *, uint8_t *, uint8_t *, int,
int, bool);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_DSP_MPEG_VIDEO_H_ */
+8 -9
View File
@@ -53,11 +53,11 @@ struct SamplingSolution {
static double ComputeWeight(double x) {
if (-1.5 < x && x < 1.5) {
if (-.5 < x && x < .5) {
return .75 - SQR(x);
return.75 - SQR(x);
} else if (x < 0) {
return .5 * SQR(x + 1.5);
return.5 * SQR(x + 1.5);
} else {
return .5 * SQR(x - 1.5);
return.5 * SQR(x - 1.5);
}
} else {
return 0;
@@ -149,12 +149,11 @@ static int Sharpen(int ax, int bx, int cx) {
static void GyaradosImpl(long dyw, long dxw, int dst[dyw][dxw], long syw,
long sxw, const int src[syw][sxw], long dyn, long dxn,
long syn, long sxn, int tmp0[restrict dyn][sxn],
int tmp1[restrict dyn][sxn],
int tmp2[restrict dyn][dxn], long yfn, long xfn,
const short fyi[dyn][yfn], const short fyw[dyn][yfn],
const short fxi[dxn][xfn], const short fxw[dxn][xfn],
bool sharpen) {
long syn, long sxn, int tmp0[dyn][sxn],
int tmp1[dyn][sxn], int tmp2[dyn][dxn], long yfn,
long xfn, const short fyi[dyn][yfn],
const short fyw[dyn][yfn], const short fxi[dxn][xfn],
const short fxw[dxn][xfn], bool sharpen) {
long i;
int eax, dy, dx, sx;
for (sx = 0; sx < sxn; ++sx) {
+3 -3
View File
@@ -106,9 +106,9 @@ void *Magkern2xY(long ys, long xs, unsigned char p[ys][xs], long yn, long xn) {
return p;
}
void *MagikarpY(long dys, long dxs, unsigned char d[restrict dys][dxs],
long sys, long sxs, const unsigned char s[sys][sxs], long yn,
long xn, const signed char K[8]) {
void *MagikarpY(long dys, long dxs, unsigned char d[dys][dxs], long sys,
long sxs, const unsigned char s[sys][sxs], long yn, long xn,
const signed char K[8]) {
long y, x;
for (y = 0; y < yn; ++y) {
for (x = 0; x < xn; ++x) {
+1
View File
@@ -39,6 +39,7 @@ EXAMPLES_BINS = \
$(EXAMPLES_COMS:%=%.dbg)
EXAMPLES_DIRECTDEPS = \
CTL \
DSP_CORE \
DSP_SCALE \
DSP_TTY \
+1 -1
View File
@@ -135,7 +135,7 @@ void sgemm(int m, int n, int k, //
const float *B, int ldb, //
float *C, int ldc) {
int nth = sysconf(_SC_NPROCESSORS_ONLN);
#pragma omp parallel for
#pragma omp parallel for schedule(static)
for (int ith = 0; ith < nth; ++ith) {
ansiBLAS tb{k, A, lda, B, ldb, C, ldc, ith, nth};
tb.matmul(m, n);
+6
View File
@@ -0,0 +1,6 @@
#include "ctl/ostream.h"
int main(int argc, char* argv[]) {
ctl::cout << "hello world\n";
return 0;
}
+1
View File
@@ -60,6 +60,7 @@ Contact: [email protected]");
#include "libc/calls/calls.h"
#include "libc/calls/termios.h"
#include "libc/calls/weirdtypes.h"
#include "libc/ctype.h"
#include "libc/errno.h"
#include "libc/log/log.h"
#include "libc/mem/alg.h"
+1
View File
@@ -14,6 +14,7 @@
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/termios.h"
#include "libc/ctype.h"
#include "libc/errno.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
-11594
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -30,6 +30,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/weirdtypes.h"
#include "libc/ctype.h"
#include "libc/errno.h"
#include "libc/log/bsd.h"
#include "libc/mem/mem.h"
+87 -86
View File
@@ -69,48 +69,49 @@ COSMOPOLITAN_C_START_
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § system calls ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
/* clang-format off */
typedef int sig_atomic_t;
bool32 isatty(int) libcesque;
char *getcwd(char *, size_t) dontthrow;
char *realpath(const char *, char *) libcesque __wur;
char *getcwd(char *, size_t) dontthrow __write_only(1, 2);
char *realpath(const char *, char *) libcesque __wur __read_only(1) __write_only(2);
char *ttyname(int) libcesque;
int access(const char *, int) libcesque;
int chdir(const char *) libcesque;
int chmod(const char *, unsigned) libcesque;
int chown(const char *, unsigned, unsigned) libcesque;
int chroot(const char *) libcesque;
int access(const char *, int) libcesque __read_only(1);
int chdir(const char *) libcesque __read_only(1);
int chmod(const char *, unsigned) libcesque __read_only(1);
int chown(const char *, unsigned, unsigned) libcesque __read_only(1);
int chroot(const char *) libcesque __read_only(1);
int close(int) libcesque;
int close_range(unsigned, unsigned, unsigned) libcesque;
int closefrom(int) libcesque;
int creat(const char *, unsigned) libcesque;
int creat(const char *, unsigned) libcesque __read_only(1);
int dup(int) libcesque;
int dup2(int, int) libcesque;
int dup3(int, int, int) libcesque;
int execl(const char *, const char *, ...) nullterminated() libcesque;
int execle(const char *, const char *, ...) nullterminated((1)) libcesque;
int execlp(const char *, const char *, ...) nullterminated() libcesque;
int execv(const char *, char *const[]) libcesque;
int execve(const char *, char *const[], char *const[]) libcesque;
int execvp(const char *, char *const[]) libcesque;
int faccessat(int, const char *, int, int) libcesque;
int execl(const char *, const char *, ...) nullterminated() libcesque __read_only(1) __read_only(2);
int execle(const char *, const char *, ...) nullterminated((1)) libcesque __read_only(1) __read_only(2);
int execlp(const char *, const char *, ...) nullterminated() libcesque __read_only(1) __read_only(2);
int execv(const char *, char *const[]) libcesque __read_only(1) __read_only(2);
int execve(const char *, char *const[], char *const[]) libcesque __read_only(1) __read_only(2) __read_only(3);
int execvp(const char *, char *const[]) libcesque __read_only(1) __read_only(2);
int faccessat(int, const char *, int, int) libcesque __read_only(2);
int fchdir(int) libcesque;
int fchmod(int, unsigned) libcesque;
int fchmodat(int, const char *, unsigned, int) libcesque;
int fchmodat(int, const char *, unsigned, int) libcesque __read_only(2);
int fchown(int, unsigned, unsigned) libcesque;
int fchownat(int, const char *, unsigned, unsigned, int) libcesque;
int fchownat(int, const char *, unsigned, unsigned, int) libcesque __read_only(2);
int fcntl(int, int, ...) libcesque;
int fdatasync(int) libcesque;
int fexecve(int, char *const[], char *const[]) libcesque;
int fexecve(int, char *const[], char *const[]) libcesque __read_only(2) __read_only(3);
int flock(int, int) libcesque;
int fork(void) libcesque;
int fsync(int) libcesque;
int ftruncate(int, int64_t) libcesque;
int getdomainname(char *, size_t) libcesque;
int getgroups(int, unsigned[]) libcesque;
int gethostname(char *, size_t) libcesque;
int getloadavg(double *, int) libcesque;
int getdomainname(char *, size_t) libcesque __write_only(1, 2);
int getgroups(int, unsigned[]) libcesque __write_only(2, 1);
int gethostname(char *, size_t) libcesque __write_only(1, 2);
int getloadavg(double *, int) libcesque __write_only(1, 2);
int getpgid(int) libcesque;
int getpgrp(void) libcesque nosideeffect;
int getpid(void) libcesque nosideeffect;
@@ -121,35 +122,35 @@ int ioctl(int, unsigned long, ...) libcesque;
int issetugid(void) libcesque;
int kill(int, int) libcesque;
int killpg(int, int) libcesque;
int lchmod(const char *, unsigned) libcesque;
int lchown(const char *, unsigned, unsigned) libcesque;
int link(const char *, const char *) libcesque;
int linkat(int, const char *, int, const char *, int) libcesque;
int mincore(void *, size_t, unsigned char *) libcesque;
int mkdir(const char *, unsigned) libcesque;
int mkdirat(int, const char *, unsigned) libcesque;
int mknod(const char *, unsigned, uint64_t) libcesque;
int lchmod(const char *, unsigned) libcesque __read_only(1);
int lchown(const char *, unsigned, unsigned) libcesque __read_only(1);
int link(const char *, const char *) libcesque __read_only(1) __read_only(2);
int linkat(int, const char *, int, const char *, int) libcesque __read_only(2) __read_only(4);
int mincore(void *, size_t, unsigned char *) libcesque __read_only(1) __write_only(3);
int mkdir(const char *, unsigned) libcesque __read_only(1);
int mkdirat(int, const char *, unsigned) libcesque __read_only(2);
int mknod(const char *, unsigned, uint64_t) libcesque __read_only(1);
int nice(int) libcesque;
int open(const char *, int, ...) libcesque;
int openat(int, const char *, int, ...) libcesque;
int open(const char *, int, ...) libcesque __read_only(1);
int openat(int, const char *, int, ...) libcesque __read_only(2);
int pause(void) libcesque;
int pipe(int[hasatleast 2]) libcesque;
int pipe2(int[hasatleast 2], int) libcesque;
int pipe(int[2]) libcesque __write_only(1);
int pipe2(int[2], int) libcesque __write_only(1);
int posix_fadvise(int, int64_t, int64_t, int) libcesque;
int posix_madvise(void *, uint64_t, int) libcesque;
int posix_madvise(void *, uint64_t, int) libcesque __read_write(1);
int raise(int) libcesque;
int reboot(int) libcesque;
int remove(const char *) libcesque;
int rename(const char *, const char *) libcesque;
int renameat(int, const char *, int, const char *) libcesque;
int rmdir(const char *) libcesque;
int remove(const char *) libcesque __read_only(1);
int rename(const char *, const char *) libcesque __read_only(1) __read_only(2);
int renameat(int, const char *, int, const char *) libcesque __read_only(2) __read_only(4);
int rmdir(const char *) libcesque __read_only(1);
int sched_yield(void) libcesque;
int setegid(unsigned) libcesque;
int seteuid(unsigned) libcesque;
int setfsgid(unsigned) libcesque;
int setfsuid(unsigned) libcesque;
int setgid(unsigned) libcesque;
int setgroups(size_t, const unsigned[]) libcesque;
int setgroups(size_t, const unsigned[]) libcesque __read_only(2);
int setpgid(int, int) libcesque;
int setpgrp(void) libcesque;
int setpriority(int, unsigned, int) libcesque;
@@ -157,32 +158,32 @@ int setregid(unsigned, unsigned) libcesque;
int setreuid(unsigned, unsigned) libcesque;
int setsid(void) libcesque;
int setuid(unsigned) libcesque;
int shm_open(const char *, int, unsigned) libcesque;
int shm_unlink(const char *) libcesque;
int shm_open(const char *, int, unsigned) libcesque __read_only(1);
int shm_unlink(const char *) libcesque __read_only(1);
int sigignore(int) libcesque;
int siginterrupt(int, int) libcesque;
int symlink(const char *, const char *) libcesque;
int symlinkat(const char *, int, const char *) libcesque;
int symlink(const char *, const char *) libcesque __read_only(1) __read_only(2);
int symlinkat(const char *, int, const char *) libcesque __read_only(1) __read_only(3);
int tcgetpgrp(int) libcesque;
int tcsetpgrp(int, int) libcesque;
int truncate(const char *, int64_t) libcesque;
int ttyname_r(int, char *, size_t) libcesque;
int unlink(const char *) libcesque;
int unlinkat(int, const char *, int) libcesque;
int truncate(const char *, int64_t) libcesque __read_only(1);
int ttyname_r(int, char *, size_t) libcesque __write_only(2, 3);
int unlink(const char *) libcesque __read_only(1);
int unlinkat(int, const char *, int) libcesque __read_only(2);
int usleep(uint64_t) libcesque;
int vfork(void) libcesque returnstwice;
int wait(int *) libcesque;
int waitpid(int, int *, int) libcesque;
int wait(int *) libcesque __write_only(1);
int waitpid(int, int *, int) libcesque __write_only(2);
int64_t clock(void) libcesque;
int64_t time(int64_t *) libcesque;
ssize_t copy_file_range(int, long *, int, long *, size_t, unsigned) libcesque;
int64_t time(int64_t *) libcesque __write_only(1);
ssize_t copy_file_range(int, long *, int, long *, size_t, unsigned) libcesque __read_write(2) __read_write(4);
ssize_t lseek(int, int64_t, int) libcesque;
ssize_t pread(int, void *, size_t, int64_t) libcesque;
ssize_t pwrite(int, const void *, size_t, int64_t) libcesque;
ssize_t read(int, void *, size_t) libcesque;
ssize_t readlink(const char *, char *, size_t) libcesque;
ssize_t readlinkat(int, const char *, char *, size_t) libcesque;
ssize_t write(int, const void *, size_t) libcesque;
ssize_t pread(int, void *, size_t, int64_t) libcesque __write_only(2, 3);
ssize_t pwrite(int, const void *, size_t, int64_t) libcesque __read_only(2);
ssize_t read(int, void *, size_t) libcesque __write_only(2, 3);
ssize_t readlink(const char *, char *, size_t) libcesque __read_only(1) __write_only(2, 3);
ssize_t readlinkat(int, const char *, char *, size_t) libcesque __read_only(2) __write_only(3, 4);
ssize_t write(int, const void *, size_t) libcesque __read_only(2);
unsigned alarm(unsigned) libcesque;
unsigned getegid(void) libcesque nosideeffect;
unsigned geteuid(void) libcesque nosideeffect;
@@ -199,50 +200,50 @@ int prctl(int, ...) libcesque;
int gettid(void) libcesque;
int setresgid(unsigned, unsigned, unsigned) libcesque;
int setresuid(unsigned, unsigned, unsigned) libcesque;
int getresgid(unsigned *, unsigned *, unsigned *) libcesque;
int getresuid(unsigned *, unsigned *, unsigned *) libcesque;
int getresgid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __write_only(2) __write_only(3);
int getresuid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __write_only(2) __write_only(3);
char *get_current_dir_name(void) libcesque __wur;
ssize_t splice(int, int64_t *, int, int64_t *, size_t, unsigned) libcesque;
int memfd_create(const char *, unsigned int) libcesque;
int execvpe(const char *, char *const[], char *const[]) libcesque;
int euidaccess(const char *, int) libcesque;
int eaccess(const char *, int) libcesque;
int madvise(void *, uint64_t, int) libcesque;
int getcpu(unsigned *, unsigned *) libcesque;
ssize_t splice(int, int64_t *, int, int64_t *, size_t, unsigned) libcesque __read_write(2) __read_write(4);
int memfd_create(const char *, unsigned int) libcesque __read_only(1);
int execvpe(const char *, char *const[], char *const[]) libcesque __read_only(1) __read_only(2) __read_only(3);
int euidaccess(const char *, int) libcesque __read_only(1);
int eaccess(const char *, int) libcesque __read_only(1);
int madvise(void *, uint64_t, int) libcesque __read_write(1);
int getcpu(unsigned *, unsigned *) libcesque __write_only(1) __write_only(2);
#endif
#ifdef _COSMO_SOURCE
bool32 fdexists(int) libcesque;
bool32 fileexists(const char *) libcesque;
bool32 fileexists(const char *) libcesque __read_only(1);
bool32 ischardev(int) libcesque;
bool32 isdirectory(const char *) libcesque;
bool32 isexecutable(const char *) libcesque;
bool32 isregularfile(const char *) libcesque;
bool32 issymlink(const char *) libcesque;
char *commandv(const char *, char *, size_t) libcesque;
int __getcwd(char *, size_t) libcesque;
bool32 isdirectory(const char *) libcesque __read_only(1);
bool32 isexecutable(const char *) libcesque __read_only(1);
bool32 isregularfile(const char *) libcesque __read_only(1);
bool32 issymlink(const char *) libcesque __read_only(1);
char *commandv(const char *, char *, size_t) libcesque __read_only(1) __write_only(2, 3);
int __getcwd(char *, size_t) libcesque __write_only(1, 2);
int clone(void *, void *, size_t, int, void *, void *, void *, void *);
int fadvise(int, uint64_t, uint64_t, int) libcesque;
int makedirs(const char *, unsigned) libcesque;
int pivot_root(const char *, const char *) libcesque;
int pledge(const char *, const char *) libcesque;
int seccomp(unsigned, unsigned, void *) libcesque;
int makedirs(const char *, unsigned) libcesque __read_only(1);
int pivot_root(const char *, const char *) libcesque __read_only(1) __read_only(2);
int pledge(const char *, const char *) libcesque __read_only(1) __read_only(2);
int seccomp(unsigned, unsigned, void *) libcesque __read_only(3);
int sys_iopl(int) libcesque;
int sys_ioprio_get(int, int) libcesque;
int sys_ioprio_set(int, int, int) libcesque;
int sys_mlock(const void *, size_t) libcesque;
int sys_mlock2(const void *, size_t, int) libcesque;
int sys_mlock(const void *, size_t) libcesque __read_only(1);
int sys_mlock2(const void *, size_t, int) libcesque __read_only(1);
int sys_mlockall(int) libcesque;
int sys_munlock(const void *, size_t) libcesque;
int sys_munlock(const void *, size_t) libcesque __read_only(1);
int sys_munlockall(void) libcesque;
int sys_personality(uint64_t) libcesque;
int sys_ptrace(int, ...) libcesque;
int sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque;
int sysctlbyname(const char *, void *, size_t *, void *, size_t) libcesque;
int sysctlnametomib(const char *, int *, size_t *) libcesque;
int sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque __read_write(1) __read_write(4) __read_write(5);
int sysctlbyname(const char *, void *, size_t *, void *, size_t) libcesque __read_only(1) __write_only(2) __read_write(3) __read_only(4);
int sysctlnametomib(const char *, int *, size_t *) libcesque __read_only(1) __write_only(2) __read_write(3);
int tmpfd(void) libcesque;
int touch(const char *, unsigned) libcesque;
int unveil(const char *, const char *) libcesque;
int touch(const char *, unsigned) libcesque __read_only(1);
int unveil(const char *, const char *) libcesque __read_only(1);
long ptrace(int, ...) libcesque;
ssize_t copyfd(int, int, size_t) libcesque;
ssize_t readansi(int, char *, size_t) libcesque;
+6 -2
View File
@@ -18,13 +18,13 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/intrin/fds.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/termios.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
@@ -258,7 +258,10 @@ static textwindows struct HostAdapterInfoNode *appendHostInfo(
node->flags = flags;
} else {
/* Copy from previous node */
node->flags = parentInfoNode->flags;
if (parentInfoNode)
node->flags = parentInfoNode->flags;
else
node->flags = 0;
}
ip = ntohl(
@@ -508,6 +511,7 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) {
}
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
bufMax = 15000; /* conservative guesstimate */
b = alloca(bufMax);
CheckLargeStackAllocation(b, bufMax);
+1
View File
@@ -144,6 +144,7 @@ textwindows int mkntenvblock(char16_t envblock[32767], char *const envp[],
n = (CountStrings(envp) + CountStrings(extravars) + 1) * sizeof(char *);
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
env.var = alloca(n);
CheckLargeStackAllocation(env.var, n);
#pragma GCC pop_options
+1 -1
View File
@@ -33,7 +33,7 @@
* @asyncsignalsafe
* @see pipe2()
*/
int pipe(int pipefd[hasatleast 2]) {
int pipe(int pipefd[2]) {
int rc;
if (!pipefd) {
// needed for windows which is polyfilled
+1 -1
View File
@@ -39,7 +39,7 @@
* @param flags can have O_CLOEXEC or O_DIRECT or O_NONBLOCK
* @return 0 on success, or -1 w/ errno and pipefd isn't modified
*/
int pipe2(int pipefd[hasatleast 2], int flags) {
int pipe2(int pipefd[2], int flags) {
int rc;
if (flags & ~(O_CLOEXEC | O_NONBLOCK | (O_DIRECT != -1u ? O_DIRECT : 0))) {
return einval();
+1
View File
@@ -60,6 +60,7 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
struct iovec *iov2;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
iov2 = alloca(iovlen * sizeof(struct iovec));
CheckLargeStackAllocation(iov2, iovlen * sizeof(struct iovec));
#pragma GCC pop_options
+1
View File
@@ -62,6 +62,7 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
struct iovec *iov2;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
iov2 = alloca(iovlen * sizeof(struct iovec));
CheckLargeStackAllocation(iov2, iovlen * sizeof(struct iovec));
#pragma GCC pop_options
+2 -1
View File
@@ -20,15 +20,16 @@
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/intrin/fds.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/cosmo.h"
#include "libc/ctype.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/nomultics.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
+2
View File
@@ -18,6 +18,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/ctype.h"
#include "libc/intrin/strace.h"
#include "libc/mem/alloca.h"
#include "libc/nt/createfile.h"
@@ -51,6 +52,7 @@ static textwindows ssize_t sys_readlinkat_nt_impl(int dirfd, const char *path,
ssize_t rc;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
uint32_t mem = 6000;
volatile char *memory = alloca(mem);
CheckLargeStackAllocation((char *)memory, mem);
+1
View File
@@ -57,6 +57,7 @@ static ssize_t readv_impl(int fd, const struct iovec *iov, int iovlen) {
struct iovec *iov2;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
iov2 = alloca(iovlen * sizeof(struct iovec));
CheckLargeStackAllocation(iov2, iovlen * sizeof(struct iovec));
#pragma GCC pop_options
+11 -24
View File
@@ -96,9 +96,8 @@ static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
if ((deliverable = pending & ~masked)) {
sig = bsfl(deliverable) + 1;
bit = 1ull << (sig - 1);
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit) {
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit)
return sig;
}
} else {
return 0;
}
@@ -107,28 +106,23 @@ static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
textwindows int __sig_get(sigset_t masked) {
int sig;
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked))) {
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked)))
sig = __sig_getter(&__sig.pending, masked);
}
return sig;
}
static textwindows bool __sig_should_use_altstack(unsigned flags,
struct CosmoTib *tib) {
if (!(flags & SA_ONSTACK)) {
if (!(flags & SA_ONSTACK))
return false; // signal handler didn't enable it
}
if (!tib->tib_sigstack_size) {
if (!tib->tib_sigstack_size)
return false; // sigaltstack() wasn't installed on this thread
}
if (tib->tib_sigstack_flags & SS_DISABLE) {
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) {
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size)
return false; // we're already on the alternate stack
}
return true;
}
@@ -282,9 +276,8 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
// jump back into original code if there aren't any pending signals
do {
if (!(sig = __sig_get(sf->ctx.uc_sigmask))) {
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
@@ -396,9 +389,7 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
int rc;
BLOCK_SIGNALS;
_pthread_ref(pt);
rc = __sig_killer(pt, sig, sic);
_pthread_unref(pt);
ALLOW_SIGNALS;
return rc;
}
@@ -461,11 +452,9 @@ textwindows void __sig_generate(int sig, int sic) {
static textwindows char *__sig_stpcpy(char *d, const char *s) {
size_t i;
for (i = 0;; ++i) {
if (!(d[i] = s[i])) {
for (i = 0;; ++i)
if (!(d[i] = s[i]))
return d + i;
}
}
}
static textwindows wontreturn void __sig_death(int sig, const char *thing) {
@@ -502,9 +491,8 @@ static textwindows void __sig_unmaskable(struct NtExceptionPointers *ep,
// 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) {
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
@@ -562,9 +550,8 @@ __msabi dontinstrument unsigned __sig_crash(struct NtExceptionPointers *ep) {
// this behavior is consistent with how unix kernels are implemented
if (sig == SIGTRAP) {
ep->ContextRecord->Rip++;
if (__sig_ignored(sig)) {
if (__sig_ignored(sig))
return kNtExceptionContinueExecution;
}
}
// win32 stack overflow detection executes INSIDE the guard page
+2 -2
View File
@@ -146,7 +146,7 @@ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
} else {
rc = sigaltstack_cosmo(neu, old);
}
STRACE("sigaltstack(%s, [%s]) → %d% m", DescribeSigaltstk(0, neu),
DescribeSigaltstk(0, old), rc);
STRACE("sigaltstack(%s, [%s]) → %d% m", DescribeSigaltstack(0, neu),
DescribeSigaltstack(0, old), rc);
return rc;
}
+57
View File
@@ -34,6 +34,63 @@
/**
* Returns information about filesystem.
*
* The `struct statfs` returned has the following fields:
*
* - `f_fstypename` holds a NUL-terminated string identifying the file
* system type. On Linux, this will usually be "nfs". On FreeBSD, it
* will usually be "zfs". On OpenBSD and NetBSD, it's usually "ffs".
* On MacOS it's usually "apfs", and on Windows it's usually "NTFS".
*
* - `f_bsize` is the optimal transfer block size. This may be used to
* appropriately chunk your i/o operations. On local file systems it
* will usually be somewhere between 4096 and 131072 bytes. With NFS
* it may be as high as 512kb.
*
* - `f_frsize` is the fragment size of the file system. This could be
* anywhere between 512 and 4096 bytes for local filesystems usually
* although it could go higher. It should less than, or equal to the
* `f_bsize`. This fragment size is what you want to use to multiply
* other fields that count blocks into a byte count.
*
* - `f_bfree` is the number of free blocks in the filesystem. You can
* multiply this number by `f_frsize` to obtain the free byte count.
*
* - `f_bavail` is the number of free blocks in the filesystem you can
* access from userspace. It's less than or equal to `f_bfree` which
* generally has some blocks reserved for root in a pinch. You could
* multiply this by `f_frsize` to convert this number to bytes.
*
* - `f_files` is the total number of file nodes. Not every OS has it.
* On Windows for instance it's currently always `INT64_MAX`. It has
* an unspecified meaning. It should be seen as informative.
*
* - `f_fsid` is an opaque data structure that uniquely identifies the
* filesystem. We're not yet certain how reliable this is across the
* various OSes and filesystem types.
*
* - `f_namelen` is basically the same as `NAME_MAX` which seems to be
* 255 on all the OSes we've evaluated. It's the maximum length when
* it comes to individual components in a filesystem path.
*
* - `f_type` is an OS-specific file system type ID. This is just some
* magic number. No defines are provided by Cosmopolitan Libc for it
*
* - `f_flags` specifies the options used when a filesystem is mounted
* and the numbers vary across OSes. Cosmopolitan Libc polyfills the
* magic numbers somewhat consistently. If `IsWindows()` is set then
* the constants defined by Microsoft (e.g. `FILE_READ_ONLY_VOLUME`)
* should be used. Otherwise on any other UNIX system, the following
* constants are provided. You should check each constant at runtime
* before using them, to determine if they're non-zero, for support.
*
* - `ST_RDONLY` if mounted in read-only mode (works UNIX + Windows)
* - `ST_NOSUID` if setuid binaries are forbidden (all UNIX support)
* - `ST_NODEV` when device file access forbidden (all UNIX support)
* - `ST_NOEXEC` when a file executions forbidden (all UNIX support)
* - `ST_SYNCHRONOUS`, if `O_SYNC` always happens (all UNIX support)
* - `ST_NOATIME` if access timestamps aren't set (all UNIX support)
* - `ST_RELATIME` if relative acces time is used (all UNIX support)
*
* @return 0 on success, or -1 w/ errno
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
+2 -2
View File
@@ -4,8 +4,8 @@
#include "libc/mem/alloca.h"
COSMOPOLITAN_C_START_
const char *DescribeSigaltstk(char[128], int, const struct sigaltstack *);
#define DescribeSigaltstk(rc, ss) DescribeSigaltstk(alloca(128), rc, ss)
const char *DescribeSigaltstack(char[128], int, const struct sigaltstack *);
#define DescribeSigaltstack(rc, ss) DescribeSigaltstack(alloca(128), rc, ss)
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGALTSTACK_INTERNAL_H_ */
+5 -1
View File
@@ -17,7 +17,6 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/intrin/fds.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
@@ -25,6 +24,7 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/strace.h"
#include "libc/mem/alloca.h"
#include "libc/nt/comms.h"
@@ -52,6 +52,7 @@ static const char *DescribeFlush(char buf[12], int action) {
}
static dontinline textwindows int sys_tcflush_nt(int fd, int queue) {
#ifdef __x86_64__
if (!sys_isatty(fd)) {
return -1; // ebadf, enotty
}
@@ -59,6 +60,9 @@ static dontinline textwindows int sys_tcflush_nt(int fd, int queue) {
return 0; // windows console output is never buffered
}
return FlushConsoleInputBytes();
#else
return enosys();
#endif
}
/**
+1
View File
@@ -62,6 +62,7 @@ ssize_t tinyprint(int fd, const char *s, ...) {
buf[n++] = c;
if (n == sizeof(buf)) {
if (tinyflush(fd, buf, n, &toto)) {
va_end(va);
return toto;
}
n = 0;
+2 -2
View File
@@ -168,8 +168,8 @@ struct ucontext {
typedef struct ucontext ucontext_t;
int getcontext(ucontext_t *) dontthrow;
int setcontext(const ucontext_t *) dontthrow;
int getcontext(ucontext_t *) dontthrow __read_write(1);
int setcontext(const ucontext_t *) dontthrow __read_only(1);
int swapcontext(ucontext_t *, const ucontext_t *) dontthrow returnstwice;
void makecontext(ucontext_t *, void *, int, ...) dontthrow dontcallback;
void __sig_restore(const ucontext_t *) wontreturn;
+1
View File
@@ -57,6 +57,7 @@ static ssize_t writev_impl(int fd, const struct iovec *iov, int iovlen) {
struct iovec *iov2;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
iov2 = alloca(iovlen * sizeof(struct iovec));
CheckLargeStackAllocation(iov2, iovlen * sizeof(struct iovec));
#pragma GCC pop_options
+7 -1
View File
@@ -2,7 +2,13 @@
#define COSMOPOLITAN_LIBC_COSMO_H_
COSMOPOLITAN_C_START_
errno_t cosmo_once(_Atomic(unsigned) *, void (*)(void)) libcesque;
#ifndef __cplusplus
#define _COSMO_ATOMIC(x) _Atomic(x)
#else
#define _COSMO_ATOMIC(x) x
#endif
errno_t cosmo_once(_COSMO_ATOMIC(unsigned) *, void (*)(void)) libcesque;
int systemvpe(const char *, char *const[], char *const[]) libcesque;
char *GetProgramExecutableName(void) libcesque;
void unleaf(void) libcesque;
+26
View File
@@ -0,0 +1,26 @@
#ifndef COSMOPOLITAN_CTYPE_H_
#define COSMOPOLITAN_CTYPE_H_
COSMOPOLITAN_C_START_
#define _tolower(u) (0040 | (u))
#define _toupper(u) (0137 & (u))
int isascii(int) libcesque;
int isspace(int) libcesque;
int isalpha(int) libcesque;
int isdigit(int) libcesque;
int isalnum(int) libcesque;
int isxdigit(int) libcesque;
int isprint(int) libcesque;
int islower(int) libcesque;
int isupper(int) libcesque;
int isblank(int) libcesque;
int iscntrl(int) libcesque;
int isgraph(int) libcesque;
int tolower(int) libcesque;
int ispunct(int) libcesque;
int toupper(int) libcesque;
int toascii(int) libcesque;
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_CTYPE_H_ */
+2
View File
@@ -557,7 +557,9 @@ static void *foreign_thunk_nt(void *func) {
// movabs $tramp,%r10
code[14] = 0x49;
code[15] = 0xba;
#ifdef __x86_64__
WRITE64LE(code + 16, (uintptr_t)__sysv2nt14);
#endif
// jmp *%r10
code[24] = 0x41;
code[25] = 0xff;
+1
View File
@@ -16,6 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/ctype.h"
#include "libc/fmt/conv.h"
#include "libc/str/str.h"
+1
View File
@@ -16,6 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/ctype.h"
#include "libc/fmt/conv.h"
#include "libc/str/str.h"
+1
View File
@@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_FMT_STRTOL_H_
#define COSMOPOLITAN_LIBC_FMT_STRTOL_H_
#include "libc/ctype.h"
#include "libc/errno.h"
#include "libc/str/str.h"
+1
View File
@@ -16,6 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/ctype.h"
#include "libc/fmt/conv.h"
#include "libc/stdckdint.h"
#include "libc/str/str.h"
+20 -76
View File
@@ -1,7 +1,3 @@
#if __GNUC__ + 0 < 2
#define __attribute__(x)
#endif
#ifndef __cplusplus
#define COSMOPOLITAN_C_START_
#define COSMOPOLITAN_C_END_
@@ -10,73 +6,16 @@
#define COSMOPOLITAN_CXX_USING_
#endif
#ifndef __ia16__
#define __far
#endif
#if !defined(__GNUC__) && __cplusplus + 0 >= 201103L
#define typeof(x) decltype(x)
#elif !defined(__GNUC__) && __STDC_VERSION__ + 0 < 201112
#define typeof(x) __typeof(x)
#endif
#ifdef __cplusplus
#if __cplusplus >= 201103L
#define _Alignof(x) alignof(x)
#endif /* C++11 */
#else /* __cplusplus */
#if __STDC_VERSION__ + 0 < 201112
#if __GNUC__ + _MSC_VER + 0
#define _Alignof(x) __alignof(x)
#else
#define _Alignof(x) /* basically all it ever did lool */ sizeof(x)
#endif /* GNU/MSVC/!ANSI */
#endif /* C11 */
#endif /* __cplusplus */
#if !defined(__cplusplus) && !defined(inline) && __STDC_VERSION__ + 0 < 199901
#if defined(__GNUC__) || defined(_MSC_VER)
#define inline __inline
#else
#define inline
#define __inline
#endif
#endif
#ifdef __chibicc__
#define __extension__
#endif
#if __STDC_VERSION__ + 0 < 201112
#ifdef __GNUC__
#define _Alignas(x) __attribute__((__aligned__(x)))
#elif defined(_MSC_VER)
#define _Alignas(x) __declspec(align(x))
#endif
#endif
#ifdef _MSC_VER
#define __builtin_unreachable() __assume(false)
#elif !((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 405 || \
defined(__clang__) || defined(__INTEL_COMPILER) || \
__has_builtin(__builtin_unreachable))
#define __builtin_unreachable() \
for (;;) { \
}
#endif
#if (!defined(__llvm__) && !__has_builtin(__builtin_assume))
#define __builtin_assume(x) \
do { \
if (!(x)) \
__builtin_unreachable(); \
} while (0)
#endif
#if __STDC_VERSION__ + 0 < 201112
#define _Atomic(t) volatile t
#endif
#ifdef __llvm__
#define __gnu_printf__ __printf__
#define __gnu_scanf__ __scanf__
@@ -325,16 +264,6 @@ typedef struct {
#define hasatleast
#endif
#if __STDC_VERSION__ + 0 < 199901L && !defined(restrict)
#if !defined(__cplusplus) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 301 || defined(_MSC_VER))
#define restrict __restrict__
#else
#define restrict
#define __restrict
#endif
#endif
#ifndef dontcallback
#if (__has_attribute(__leaf__) || \
(!defined(__llvm__) && \
@@ -438,6 +367,24 @@ typedef struct {
#define offsetof(type, member) __builtin_offsetof(type, member)
#if defined(__GNUC__) && __GNUC__ >= 10
#define __read_only(...) __attribute__((__access__(__read_only__, __VA_ARGS__)))
#define __write_only(...) \
__attribute__((__access__(__write_only__, __VA_ARGS__)))
#define __read_write(...) \
__attribute__((__access__(__read_write__, __VA_ARGS__)))
#else
#define __read_only(...)
#define __write_only(...)
#define __read_write(...)
#endif
#if defined(__GNUC__) && __GNUC__ >= 13
#define __fd_arg(N) __attribute__((__fd_arg__(N)))
#else
#define __fd_arg(N)
#endif
#ifdef _COSMO_SOURCE
#ifndef dontinstrument
@@ -594,11 +541,8 @@ typedef struct {
#ifdef __aarch64__
/* raise sigill (not sigtrap) like x86 does */
#define __builtin_trap() \
do { \
__asm__("udf\t#0x666"); \
__builtin_unreachable(); \
} while (0)
#define __builtin_trap() \
(({ __asm__("udf\t#0x666"); }), __builtin_unreachable())
#endif
#endif /* _COSMO_SOURCE */
+2 -6
View File
@@ -3,8 +3,8 @@
#endif
#define __COSMOPOLITAN_MAJOR__ 3
#define __COSMOPOLITAN_MINOR__ 5
#define __COSMOPOLITAN_PATCH__ 7
#define __COSMOPOLITAN_MINOR__ 6
#define __COSMOPOLITAN_PATCH__ 2
#define __COSMOPOLITAN__ \
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
__COSMOPOLITAN_PATCH__)
@@ -83,10 +83,6 @@
#define __BIGGEST_ALIGNMENT__ 16
#endif
#ifdef _COSMO_SOURCE
#define _PAGESIZE 4096
#endif
#if defined(__LP64__) && !defined(__INT64_TYPE__)
#include "libc/integral/lp64.inc"
#elif defined(_MSC_VER) && !defined(__INT64_TYPE__)
+12 -9
View File
@@ -18,6 +18,9 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/cp.internal.h"
#include "libc/intrin/describebacktrace.h"
#include "libc/intrin/kprintf.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/posixthread.internal.h"
@@ -26,11 +29,9 @@
int begin_cancelation_point(void) {
int state = 0;
struct CosmoTib *tib;
struct PosixThread *pt;
if (__tls_enabled) {
tib = __get_tls();
if ((pt = (struct PosixThread *)tib->tib_pthread)) {
struct PosixThread *pt;
if ((pt = _pthread_self())) {
state = pt->pt_flags & PT_INCANCEL;
pt->pt_flags |= PT_INCANCEL;
}
@@ -39,18 +40,20 @@ int begin_cancelation_point(void) {
}
void end_cancelation_point(int state) {
struct CosmoTib *tib;
struct PosixThread *pt;
if (__tls_enabled) {
tib = __get_tls();
if ((pt = (struct PosixThread *)tib->tib_pthread)) {
struct PosixThread *pt;
if ((pt = _pthread_self())) {
pt->pt_flags &= ~PT_INCANCEL;
pt->pt_flags |= state;
}
}
}
void report_cancelation_point(void) {
void report_cancelation_point(int sysv_ordinal, int xnu_ordinal) {
char bt[160];
struct StackFrame *bp = __builtin_frame_address(0);
kprintf("error: report_cancelation_point(%#x, %#x) %s\n", sysv_ordinal,
xnu_ordinal, (DescribeBacktrace)(bt, bp));
__builtin_trap();
}
+4 -4
View File
@@ -24,13 +24,13 @@
#define N 160
static bool IsDangerous(const void *ptr) {
privileged static bool IsDangerous(const void *ptr) {
if (_weaken(kisdangerous))
return _weaken(kisdangerous)(ptr);
return false;
}
static char *FormatHex(char *p, unsigned long x) {
privileged static char *FormatHex(char *p, unsigned long x) {
int k = x ? (__builtin_clzl(x) ^ 63) + 1 : 1;
k = (k + 3) & -4;
while (k > 0)
@@ -39,8 +39,8 @@ static char *FormatHex(char *p, unsigned long x) {
return p;
}
dontinstrument const char *(DescribeBacktrace)(char buf[N],
const struct StackFrame *fr) {
privileged dontinstrument const char *(
DescribeBacktrace)(char buf[N], const struct StackFrame *fr) {
char *p = buf;
char *pe = p + N;
bool gotsome = false;
+4
View File
@@ -27,6 +27,7 @@ const char *DescribeItimer(char[12], int) libcesque;
const char *DescribeMapFlags(char[64], int) libcesque;
const char *DescribeMapping(char[8], int, int) libcesque;
const char *DescribeMremapFlags(char[30], int) libcesque;
const char *DescribeMsyncFlags(char[48], int) libcesque;
const char *DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
const char *DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
const char *DescribeNtCreationDisposition(uint32_t) libcesque;
@@ -54,6 +55,7 @@ const char *DescribeRlimitName(char[20], int) libcesque;
const char *DescribeSchedPolicy(char[48], int) libcesque;
const char *DescribeSeccompOperation(int) libcesque;
const char *DescribeSiCode(char[20], int, int) libcesque;
const char *DescribeSigaltstackFlags(char[22], int) libcesque;
const char *DescribeSleepFlags(char[16], int) libcesque;
const char *DescribeSockLevel(char[12], int) libcesque;
const char *DescribeSockOptname(char[32], int, int) libcesque;
@@ -82,6 +84,7 @@ const char *DescribeWhichPrio(char[12], int) libcesque;
#define DescribeMapFlags(x) DescribeMapFlags(alloca(64), x)
#define DescribeMapping(x, y) DescribeMapping(alloca(8), x, y)
#define DescribeMremapFlags(x) DescribeMremapFlags(alloca(30), x)
#define DescribeMsyncFlags(x) DescribeMsyncFlags(alloca(48), x)
#define DescribeNtConsoleInFlags(x) DescribeNtConsoleInFlags(alloca(256), x)
#define DescribeNtConsoleOutFlags(x) DescribeNtConsoleOutFlags(alloca(128), x)
#define DescribeNtFileAccessFlags(x) DescribeNtFileAccessFlags(alloca(512), x)
@@ -107,6 +110,7 @@ const char *DescribeWhichPrio(char[12], int) libcesque;
#define DescribeRlimitName(rl) DescribeRlimitName(alloca(20), rl)
#define DescribeSchedPolicy(x) DescribeSchedPolicy(alloca(48), x)
#define DescribeSiCode(x, y) DescribeSiCode(alloca(20), x, y)
#define DescribeSigaltstackFlags(x) DescribeSigaltstackFlags(alloca(22), x)
#define DescribeSleepFlags(x) DescribeSleepFlags(alloca(16), x)
#define DescribeSockLevel(x) DescribeSockLevel(alloca(12), x)
#define DescribeSockOptname(x, y) DescribeSockOptname(alloca(32), x, y)
@@ -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 │
@@ -16,20 +16,15 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/intrin/describeflags.h"
#include "libc/macros.internal.h"
#include "libc/sysv/consts/msync.h"
static bool _pthread_deref(struct PosixThread *pt) {
int refs = atomic_load_explicit(&pt->pt_refs, memory_order_acquire);
return !refs || !atomic_fetch_sub(&pt->pt_refs, 1);
}
void _pthread_unref(struct PosixThread *pt) {
if (_pthread_deref(pt)) {
unassert(_weaken(_pthread_free));
_weaken(_pthread_free)(pt, false);
_weaken(_pthread_decimate)();
}
const char *(DescribeMsyncFlags)(char buf[48], int x) {
const struct DescribeFlags kMsyncFlags[] = {
{MS_SYNC, "SYNC"}, //
{MS_ASYNC, "ASYNC"}, //
{MS_INVALIDATE, "INVALIDATE"}, //
};
return DescribeFlags(buf, 48, kMsyncFlags, ARRAYLEN(kMsyncFlags), "MS_", x);
}
+4 -4
View File
@@ -21,8 +21,8 @@
#include "libc/intrin/describeflags.h"
#include "libc/intrin/kprintf.h"
const char *(DescribeSigaltstk)(char buf[128], int rc,
const struct sigaltstack *ss) {
const char *(DescribeSigaltstack)(char buf[128], int rc,
const struct sigaltstack *ss) {
if (rc == -1)
return "n/a";
if (!ss)
@@ -30,8 +30,8 @@ const char *(DescribeSigaltstk)(char buf[128], int rc,
if (kisdangerous(ss)) {
ksnprintf(buf, 128, "%p", ss);
} else {
ksnprintf(buf, 128, "{.ss_sp=%p, .ss_flags=%#lx, .ss_size=%'zu}", ss->ss_sp,
ss->ss_flags, ss->ss_size);
ksnprintf(buf, 128, "{.ss_sp=%p, .ss_flags=%s, .ss_size=%'zu}", ss->ss_sp,
DescribeSigaltstackFlags(ss->ss_flags), ss->ss_size);
}
return buf;
}
+30
View File
@@ -0,0 +1,30 @@
/*-*- 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/intrin/describeflags.h"
#include "libc/macros.internal.h"
#include "libc/sysv/consts/ss.h"
const char *(DescribeSigaltstackFlags)(char buf[22], int x) {
const struct DescribeFlags kSigaltstackFlags[] = {
{SS_ONSTACK, "ONSTACK"}, //
{SS_DISABLE, "DISABLE"}, //
};
return DescribeFlags(buf, 48, kSigaltstackFlags, ARRAYLEN(kSigaltstackFlags),
"SS_", x);
}
+1 -1
View File
@@ -20,7 +20,7 @@
// returns true if `p` is preceded by x86 call instruction
// this is actually impossible to do but we'll do our best
dontinstrument int __is_call(const unsigned char *p) {
privileged dontinstrument int __is_call(const unsigned char *p) {
if (p[-5] == 0xe8)
return 5; // call Jvds
if (p[-2] == 0xff && (p[-1] & 070) == 020)
+1
View File
@@ -65,6 +65,7 @@
#include "libc/thread/tls.h"
#include "libc/thread/tls2.internal.h"
#include "libc/vga/vga.internal.h"
#include "libc/wctype.h"
#define STACK_ERROR "kprintf error: stack is about to overflow\n"
+57 -14
View File
@@ -18,13 +18,17 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/intrin/maps.h"
#include "ape/sections.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/dce.h"
#include "libc/intrin/describebacktrace.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/maps.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/lock.h"
#ifdef __x86_64__
__static_yoink("_init_maps");
@@ -85,28 +89,67 @@ void __maps_init(void) {
}
privileged bool __maps_lock(void) {
int me;
uint64_t word, lock;
struct CosmoTib *tib;
if (!__tls_enabled)
return false;
tib = __get_tls_privileged();
if (atomic_fetch_add_explicit(&tib->tib_relock_maps, 1, memory_order_relaxed))
return true;
while (atomic_exchange_explicit(&__maps.lock, 1, memory_order_acquire)) {
#if defined(__GNUC__) && defined(__aarch64__)
__asm__ volatile("yield");
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
__asm__ volatile("pause");
#endif
if (!(tib = __get_tls_privileged()))
return false;
if (tib->tib_flags & TIB_FLAG_VFORKED)
return false;
me = atomic_load_explicit(&tib->tib_tid, memory_order_acquire);
if (me <= 0)
return false;
word = atomic_load_explicit(&__maps.lock, memory_order_relaxed);
for (;;) {
if (MUTEX_OWNER(word) == me) {
if (atomic_compare_exchange_weak_explicit(
&__maps.lock, &word, MUTEX_INC_DEPTH(word), memory_order_relaxed,
memory_order_relaxed))
return true;
continue;
}
word = 0;
lock = MUTEX_LOCK(word);
lock = MUTEX_SET_OWNER(lock, me);
if (atomic_compare_exchange_weak_explicit(&__maps.lock, &word, lock,
memory_order_acquire,
memory_order_relaxed))
return false;
for (;;) {
word = atomic_load_explicit(&__maps.lock, memory_order_relaxed);
if (MUTEX_OWNER(word) == me)
break;
if (!word)
break;
}
}
return false;
}
privileged void __maps_unlock(void) {
int me;
uint64_t word;
struct CosmoTib *tib;
if (!__tls_enabled)
return;
tib = __get_tls_privileged();
if (atomic_fetch_sub_explicit(&tib->tib_relock_maps, 1,
memory_order_relaxed) == 1)
atomic_store_explicit(&__maps.lock, 0, memory_order_release);
if (!(tib = __get_tls_privileged()))
return;
if (tib->tib_flags & TIB_FLAG_VFORKED)
return;
me = atomic_load_explicit(&tib->tib_tid, memory_order_acquire);
if (me <= 0)
return;
word = atomic_load_explicit(&__maps.lock, memory_order_relaxed);
for (;;) {
if (MUTEX_DEPTH(word)) {
if (atomic_compare_exchange_weak_explicit(
&__maps.lock, &word, MUTEX_DEC_DEPTH(word), memory_order_relaxed,
memory_order_relaxed))
break;
}
if (atomic_compare_exchange_weak_explicit(
&__maps.lock, &word, 0, memory_order_release, memory_order_relaxed))
break;
}
}
+5 -3
View File
@@ -6,6 +6,8 @@
#include "libc/thread/tls2.internal.h"
COSMOPOLITAN_C_START_
#define MAPS_RETRY ((void *)-1)
#define MAP_TREE_CONTAINER(e) TREE_CONTAINER(struct Map, tree, e)
struct Map {
@@ -20,14 +22,14 @@ struct Map {
intptr_t hand; /* windows nt only */
union {
struct Tree tree;
struct Map *free;
struct Map *freed;
};
};
struct Maps {
atomic_int lock;
struct Tree *maps;
_Atomic(struct Map *) free;
_Atomic(uint64_t) lock;
_Atomic(struct Map *) freed;
size_t count;
size_t pages;
_Atomic(char *) pick;
+55 -42
View File
@@ -16,15 +16,9 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "ape/sections.internal.h"
#include "libc/atomic.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
@@ -34,26 +28,23 @@
#include "libc/intrin/kprintf.h"
#include "libc/intrin/maps.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/tree.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/stdio/rand.h"
#include "libc/stdio/sysparam.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/mremap.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#define MMDEBUG IsModeDbg()
#define MAX_SIZE 0x0ff800000000ul
#define MAX_TRIES 10
#define MAX_TRIES 50
#define MAP_FIXED_NOREPLACE_linux 0x100000
@@ -129,6 +120,7 @@ static int __muntrack(char *addr, size_t size, int pagesz,
struct Map *map;
struct Map *next;
struct Map *floor;
StartOver:
floor = __maps_floor(addr);
for (map = floor; map && map->addr <= addr + size; map = next) {
next = __maps_next(map);
@@ -140,7 +132,7 @@ static int __muntrack(char *addr, size_t size, int pagesz,
if (addr <= map_addr && addr + PGUP(size) >= map_addr + PGUP(map_size)) {
// remove mapping completely
tree_remove(&__maps.maps, &map->tree);
map->free = *deleted;
map->freed = *deleted;
*deleted = map;
__maps.pages -= (map_size + pagesz - 1) / pagesz;
__maps.count -= 1;
@@ -157,6 +149,8 @@ static int __muntrack(char *addr, size_t size, int pagesz,
ASSERT(left > 0);
struct Map *leftmap;
if ((leftmap = __maps_alloc())) {
if (leftmap == MAPS_RETRY)
goto StartOver;
map->addr += left;
map->size = right;
if (!(map->flags & MAP_ANONYMOUS))
@@ -164,7 +158,7 @@ static int __muntrack(char *addr, size_t size, int pagesz,
__maps.pages -= (left + pagesz - 1) / pagesz;
leftmap->addr = map_addr;
leftmap->size = left;
leftmap->free = *deleted;
leftmap->freed = *deleted;
*deleted = leftmap;
__maps_check();
} else {
@@ -176,11 +170,13 @@ static int __muntrack(char *addr, size_t size, int pagesz,
size_t right = map_addr + map_size - addr;
struct Map *rightmap;
if ((rightmap = __maps_alloc())) {
if (rightmap == MAPS_RETRY)
goto StartOver;
map->size = left;
__maps.pages -= (right + pagesz - 1) / pagesz;
rightmap->addr = addr;
rightmap->size = right;
rightmap->free = *deleted;
rightmap->freed = *deleted;
*deleted = rightmap;
__maps_check();
} else {
@@ -193,8 +189,14 @@ static int __muntrack(char *addr, size_t size, int pagesz,
size_t right = map_size - middle - left;
struct Map *leftmap;
if ((leftmap = __maps_alloc())) {
if (leftmap == MAPS_RETRY)
goto StartOver;
struct Map *middlemap;
if ((middlemap = __maps_alloc())) {
if (middlemap == MAPS_RETRY) {
__maps_free(leftmap);
goto StartOver;
}
leftmap->addr = map_addr;
leftmap->size = left;
leftmap->off = map->off;
@@ -209,10 +211,11 @@ static int __muntrack(char *addr, size_t size, int pagesz,
__maps.count += 1;
middlemap->addr = addr;
middlemap->size = size;
middlemap->free = *deleted;
middlemap->freed = *deleted;
*deleted = middlemap;
__maps_check();
} else {
__maps_free(leftmap);
rc = -1;
}
} else {
@@ -226,9 +229,9 @@ static int __muntrack(char *addr, size_t size, int pagesz,
void __maps_free(struct Map *map) {
map->size = 0;
map->addr = MAP_FAILED;
map->free = atomic_load_explicit(&__maps.free, memory_order_relaxed);
map->freed = atomic_load_explicit(&__maps.freed, memory_order_relaxed);
for (;;) {
if (atomic_compare_exchange_weak_explicit(&__maps.free, &map->free, map,
if (atomic_compare_exchange_weak_explicit(&__maps.freed, &map->freed, map,
memory_order_release,
memory_order_relaxed))
break;
@@ -238,7 +241,7 @@ void __maps_free(struct Map *map) {
static void __maps_free_all(struct Map *list) {
struct Map *next;
for (struct Map *map = list; map; map = next) {
next = map->free;
next = map->freed;
__maps_free(map);
}
}
@@ -294,9 +297,9 @@ static void __maps_insert(struct Map *map) {
struct Map *__maps_alloc(void) {
struct Map *map;
map = atomic_load_explicit(&__maps.free, memory_order_relaxed);
map = atomic_load_explicit(&__maps.freed, memory_order_relaxed);
while (map) {
if (atomic_compare_exchange_weak_explicit(&__maps.free, &map, map->free,
if (atomic_compare_exchange_weak_explicit(&__maps.freed, &map, map->freed,
memory_order_acquire,
memory_order_relaxed))
return map;
@@ -313,12 +316,11 @@ struct Map *__maps_alloc(void) {
map->flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NOFORK;
map->hand = sys.maphandle;
__maps_lock();
__maps_insert(map++);
__maps_insert(map);
__maps_unlock();
map->addr = MAP_FAILED;
for (int i = 1; i < gransz / sizeof(struct Map) - 1; ++i)
for (int i = 1; i < gransz / sizeof(struct Map); ++i)
__maps_free(map + i);
return map;
return MAPS_RETRY;
}
static int __munmap(char *addr, size_t size) {
@@ -355,7 +357,7 @@ static int __munmap(char *addr, size_t size) {
// delete mappings
int rc = 0;
for (struct Map *map = deleted; map; map = map->free) {
for (struct Map *map = deleted; map; map = map->freed) {
if (!IsWindows()) {
if (sys_munmap(map->addr, map->size))
rc = -1;
@@ -368,7 +370,7 @@ static int __munmap(char *addr, size_t size) {
}
}
// free mappings
// freed mappings
__maps_free_all(deleted);
return rc;
@@ -377,7 +379,7 @@ static int __munmap(char *addr, size_t size) {
void *__maps_randaddr(void) {
uintptr_t addr;
addr = _rand64();
addr &= 0x007fffffffff;
addr &= 0x3fffffffffff;
addr |= 0x004000000000;
addr &= -__gransize;
return (void *)addr;
@@ -405,21 +407,32 @@ void *__maps_pickaddr(size_t size) {
static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd,
int64_t off, int pagesz, int gransz) {
// allocate Map object
struct Map *map;
do {
if (!(map = __maps_alloc()))
return MAP_FAILED;
} while (map == MAPS_RETRY);
// polyfill nuances of fixed mappings
int sysflags = flags;
bool noreplace = false;
bool should_untrack = false;
if (flags & MAP_FIXED_NOREPLACE) {
if (flags & MAP_FIXED)
if (flags & MAP_FIXED) {
__maps_free(map);
return (void *)einval();
}
sysflags &= ~MAP_FIXED_NOREPLACE;
if (IsLinux()) {
noreplace = true;
sysflags |= MAP_FIXED_NOREPLACE_linux;
} else if (IsFreebsd() || IsNetbsd()) {
sysflags |= MAP_FIXED;
if (__maps_overlaps(addr, size, pagesz))
if (__maps_overlaps(addr, size, pagesz)) {
__maps_free(map);
return (void *)eexist();
}
} else {
noreplace = true;
}
@@ -427,11 +440,6 @@ static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd,
should_untrack = true;
}
// allocate Map object
struct Map *map;
if (!(map = __maps_alloc()))
return MAP_FAILED;
// remove mapping we blew away
if (IsWindows() && should_untrack)
__munmap(addr, size);
@@ -460,7 +468,7 @@ TryAgain:
}
// polyfill map fixed noreplace
// we assume non-linux gives us addr if it's free
// we assume non-linux gives us addr if it's freed
// that's what linux (e.g. rhel7) did before noreplace
if (noreplace && res.addr != addr) {
if (!IsWindows()) {
@@ -581,23 +589,27 @@ static void *__mremap_impl(char *old_addr, size_t old_size, size_t new_size,
return (void *)einval();
}
// allocate object for tracking new mapping
struct Map *map;
do {
if (!(map = __maps_alloc()))
return (void *)enomem();
} while (map == MAPS_RETRY);
// check old interval is fully contained within one mapping
struct Map *old_map;
if (!(old_map = __maps_floor(old_addr)) ||
old_addr + old_size > old_map->addr + PGUP(old_map->size) ||
old_addr < old_map->addr)
old_addr < old_map->addr) {
__maps_free(map);
return (void *)efault();
}
// save old properties
int old_off = old_map->off;
int old_prot = old_map->prot;
int old_flags = old_map->flags;
// allocate object for tracking new mapping
struct Map *map;
if (!(map = __maps_alloc()))
return (void *)enomem();
// netbsd mremap fixed returns enoent rather than unmapping old pages
if (IsNetbsd() && (flags & MREMAP_FIXED))
if (__munmap(new_addr, new_size)) {
@@ -843,7 +855,8 @@ void *mremap(void *old_addr, size_t old_size, size_t new_size, int flags, ...) {
*/
int munmap(void *addr, size_t size) {
int rc = __munmap(addr, size);
STRACE("munmap(%p, %'zu) → %d% m", addr, size, rc);
STRACE("munmap(%p, %'zu) → %d% m (%'zu bytes total)", addr, size, rc,
__maps.pages * __pagesize);
return rc;
}
+12 -1
View File
@@ -75,6 +75,7 @@ int __mprotect(char *addr, size_t size, int prot) {
return edeadlk();
}
struct Map *map, *floor;
StartOver:
floor = __maps_floor(addr);
for (map = floor; map && map->addr <= addr + size; map = __maps_next(map)) {
char *map_addr = map->addr;
@@ -93,10 +94,12 @@ int __mprotect(char *addr, size_t size, int prot) {
}
} else if (addr <= map_addr) {
// change lefthand side of mapping
size_t left = PGUP(addr + size - map_addr);
size_t left = addr + size - map_addr;
size_t right = map_size - left;
struct Map *leftmap;
if ((leftmap = __maps_alloc())) {
if (leftmap == MAPS_RETRY)
goto StartOver;
if (!__mprotect_chunk(map_addr, left, prot, false)) {
leftmap->addr = map_addr;
leftmap->size = left;
@@ -127,6 +130,8 @@ int __mprotect(char *addr, size_t size, int prot) {
size_t right = map_addr + map_size - addr;
struct Map *leftmap;
if ((leftmap = __maps_alloc())) {
if (leftmap == MAPS_RETRY)
goto StartOver;
if (!__mprotect_chunk(map_addr + left, right, prot, false)) {
leftmap->addr = map_addr;
leftmap->size = left;
@@ -159,8 +164,14 @@ int __mprotect(char *addr, size_t size, int prot) {
size_t right = map_size - middle - left;
struct Map *leftmap;
if ((leftmap = __maps_alloc())) {
if (leftmap == MAPS_RETRY)
goto StartOver;
struct Map *midlmap;
if ((midlmap = __maps_alloc())) {
if (midlmap == MAPS_RETRY) {
__maps_free(leftmap);
goto StartOver;
}
if (!__mprotect_chunk(map_addr + left, middle, prot, false)) {
leftmap->addr = map_addr;
leftmap->size = left;
+3 -1
View File
@@ -23,6 +23,7 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/macros.internal.h"
#include "libc/sysv/errfuns.h"
@@ -93,6 +94,7 @@ int msync(void *addr, size_t size, int flags) {
END_CANCELATION_POINT;
Finished:
STRACE("msync(%p, %'zu, %#x) → %d% m", addr, size, flags, rc);
STRACE("msync(%p, %'zu, %s) → %d% m", addr, size, DescribeMsyncFlags(flags),
rc);
return rc;
}
+3 -1
View File
@@ -22,8 +22,10 @@
/**
* Destroys mutex.
*
* Destroying a mutex that's currently locked or being waited upon, will
* result in undefined behavior.
*
* @return 0 on success, or error number on failure
* @raise EINVAL if mutex is locked in our implementation
*/
errno_t pthread_mutex_destroy(pthread_mutex_t *mutex) {
memset(mutex, -1, sizeof(*mutex));
+74 -27
View File
@@ -16,6 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/dce.h"
@@ -27,39 +28,48 @@
#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_lock_impl(pthread_mutex_t *mutex) {
int me;
static void pthread_mutex_lock_spin(atomic_int *word) {
int backoff = 0;
uint64_t word, lock;
// get current state of lock
word = atomic_load_explicit(&mutex->_word, memory_order_relaxed);
// use fancy nsync mutex if possible
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL && //
MUTEX_PSHARED(word) == PTHREAD_PROCESS_PRIVATE && //
_weaken(nsync_mu_lock)) {
_weaken(nsync_mu_lock)((nsync_mu *)mutex);
return 0;
}
// implement barebones normal mutexes
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
for (;;) {
if (!atomic_exchange_explicit(word, 1, memory_order_acquire))
break;
for (;;) {
word = MUTEX_UNLOCK(word);
lock = MUTEX_LOCK(word);
if (atomic_compare_exchange_weak_explicit(&mutex->_word, &word, lock,
memory_order_acquire,
memory_order_relaxed))
return 0;
backoff = pthread_delay_np(mutex, backoff);
if (!atomic_load_explicit(word, memory_order_relaxed))
break;
backoff = pthread_delay_np(word, backoff);
}
}
}
// implement recursive mutexes
me = gettid();
// 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) {
int word;
for (int i = 0; i < 4; ++i) {
word = 0;
if (atomic_compare_exchange_strong_explicit(
futex, &word, 1, memory_order_acquire, memory_order_acquire))
return;
pthread_pause_np();
}
if (word == 1)
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
while (word > 0) {
BLOCK_CANCELATION;
_weaken(nsync_futex_wait_)(futex, 2, pshare, 0);
ALLOW_CANCELATION;
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
}
}
static errno_t pthread_mutex_lock_recursive(pthread_mutex_t *mutex,
uint64_t word) {
uint64_t lock;
int backoff = 0;
int me = gettid();
for (;;) {
if (MUTEX_OWNER(word) == me) {
if (MUTEX_TYPE(word) != PTHREAD_MUTEX_ERRORCHECK) {
@@ -85,10 +95,47 @@ static errno_t pthread_mutex_lock_impl(pthread_mutex_t *mutex) {
mutex->_pid = __pid;
return 0;
}
backoff = pthread_delay_np(mutex, backoff);
for (;;) {
word = atomic_load_explicit(&mutex->_word, memory_order_relaxed);
if (MUTEX_OWNER(word) == me)
break;
if (word == MUTEX_UNLOCK(word))
break;
backoff = pthread_delay_np(mutex, backoff);
}
}
}
static errno_t pthread_mutex_lock_impl(pthread_mutex_t *mutex) {
uint64_t word;
// get current state of lock
word = atomic_load_explicit(&mutex->_word, memory_order_relaxed);
#if PTHREAD_USE_NSYNC
// use superior mutexes if possible
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL && //
MUTEX_PSHARED(word) == PTHREAD_PROCESS_PRIVATE && //
_weaken(nsync_mu_lock)) {
_weaken(nsync_mu_lock)((nsync_mu *)mutex);
return 0;
}
#endif
// 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);
}
return 0;
}
// handle recursive and error checking mutexes
return pthread_mutex_lock_recursive(mutex, word);
}
/**
* Locks mutex.
*
+61 -42
View File
@@ -24,52 +24,27 @@
#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"
/**
* Attempts acquiring lock.
*
* Unlike pthread_mutex_lock() this function won't block and instead
* returns an error immediately if the lock couldn't be acquired.
*
* @return 0 if lock was acquired, otherwise an errno
* @raise EAGAIN if maximum number of recursive locks is held
* @raise EBUSY if lock is currently held in read or write mode
* @raise EINVAL if `mutex` doesn't refer to an initialized lock
* @raise EDEADLK if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the
* current thread already holds this mutex
*/
errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
int me;
uint64_t word, lock;
static errno_t pthread_mutex_trylock_spin(atomic_int *word) {
if (!atomic_exchange_explicit(word, 1, memory_order_acquire))
return 0;
return EBUSY;
}
// get current state of lock
word = atomic_load_explicit(&mutex->_word, memory_order_relaxed);
static errno_t pthread_mutex_trylock_drepper(atomic_int *futex) {
int word = 0;
if (atomic_compare_exchange_strong_explicit(
futex, &word, 1, memory_order_acquire, memory_order_acquire))
return 0;
return EBUSY;
}
// delegate to *NSYNC if possible
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL &&
MUTEX_PSHARED(word) == PTHREAD_PROCESS_PRIVATE && //
_weaken(nsync_mu_trylock)) {
if (_weaken(nsync_mu_trylock)((nsync_mu *)mutex)) {
return 0;
} else {
return EBUSY;
}
}
// handle normal mutexes
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
word = MUTEX_UNLOCK(word);
lock = MUTEX_LOCK(word);
if (atomic_compare_exchange_weak_explicit(&mutex->_word, &word, lock,
memory_order_acquire,
memory_order_relaxed))
return 0;
return EBUSY;
}
// handle recursive and error check mutexes
me = gettid();
static errno_t pthread_mutex_trylock_recursive(pthread_mutex_t *mutex,
uint64_t word) {
uint64_t lock;
int me = gettid();
for (;;) {
if (MUTEX_OWNER(word) == me) {
if (MUTEX_TYPE(word) != PTHREAD_MUTEX_ERRORCHECK) {
@@ -98,3 +73,47 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
return EBUSY;
}
}
/**
* Attempts acquiring lock.
*
* Unlike pthread_mutex_lock() this function won't block and instead
* returns an error immediately if the lock couldn't be acquired.
*
* @return 0 if lock was acquired, otherwise an errno
* @raise EAGAIN if maximum number of recursive locks is held
* @raise EBUSY if lock is currently held in read or write mode
* @raise EINVAL if `mutex` doesn't refer to an initialized lock
* @raise EDEADLK if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the
* current thread already holds this mutex
*/
errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
// get current state of lock
uint64_t word = atomic_load_explicit(&mutex->_word, memory_order_relaxed);
#if PTHREAD_USE_NSYNC
// use superior mutexes if possible
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL &&
MUTEX_PSHARED(word) == PTHREAD_PROCESS_PRIVATE && //
_weaken(nsync_mu_trylock)) {
if (_weaken(nsync_mu_trylock)((nsync_mu *)mutex)) {
return 0;
} else {
return EBUSY;
}
}
#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);
}
}
// handle recursive and error checking mutexes
return pthread_mutex_trylock_recursive(mutex, word);
}
+55 -32
View File
@@ -25,43 +25,25 @@
#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"
/**
* Releases mutex.
*
* This function does nothing in vfork() children.
*
* @return 0 on success or error number on failure
* @raises EPERM if in error check mode and not owned by caller
* @vforksafe
*/
errno_t pthread_mutex_unlock(pthread_mutex_t *mutex) {
int me;
uint64_t word, lock;
static void pthread_mutex_unlock_spin(atomic_int *word) {
atomic_store_explicit(word, 0, memory_order_release);
}
LOCKTRACE("pthread_mutex_unlock(%t)", mutex);
// get current state of lock
word = atomic_load_explicit(&mutex->_word, memory_order_relaxed);
// use fancy nsync mutex if possible
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL && //
MUTEX_PSHARED(word) == PTHREAD_PROCESS_PRIVATE && //
_weaken(nsync_mu_unlock)) {
_weaken(nsync_mu_unlock)((nsync_mu *)mutex);
return 0;
// 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);
}
}
// implement barebones normal mutexes
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
lock = MUTEX_UNLOCK(word);
atomic_store_explicit(&mutex->_word, lock, memory_order_release);
return 0;
}
// implement recursive mutex unlocking
me = gettid();
static errno_t pthread_mutex_unlock_recursive(pthread_mutex_t *mutex,
uint64_t word) {
int me = gettid();
for (;;) {
// we allow unlocking an initialized lock that wasn't locked, but we
@@ -86,3 +68,44 @@ errno_t pthread_mutex_unlock(pthread_mutex_t *mutex) {
return 0;
}
}
/**
* Releases mutex.
*
* This function does nothing in vfork() children.
*
* @return 0 on success or error number on failure
* @raises EPERM if in error check mode and not owned by caller
* @vforksafe
*/
errno_t pthread_mutex_unlock(pthread_mutex_t *mutex) {
uint64_t word;
LOCKTRACE("pthread_mutex_unlock(%t)", mutex);
// get current state of lock
word = atomic_load_explicit(&mutex->_word, memory_order_relaxed);
#if PTHREAD_USE_NSYNC
// use superior mutexes if possible
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL && //
MUTEX_PSHARED(word) == PTHREAD_PROCESS_PRIVATE && //
_weaken(nsync_mu_unlock)) {
_weaken(nsync_mu_unlock)((nsync_mu *)mutex);
return 0;
}
#endif
// 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);
}
return 0;
}
// handle recursive and error checking mutexes
return pthread_mutex_unlock_recursive(mutex, word);
}
+6 -2
View File
@@ -38,8 +38,12 @@
* @see pthread_spin_init
*/
errno_t pthread_spin_lock(pthread_spinlock_t *spin) {
while (atomic_exchange_explicit(&spin->_lock, 1, memory_order_acquire)) {
pthread_pause_np();
for (;;) {
if (!atomic_exchange_explicit(&spin->_lock, 1, memory_order_acquire))
break;
for (;;)
if (!atomic_load_explicit(&spin->_lock, memory_order_relaxed))
break;
}
return 0;
}
+7 -2
View File
@@ -22,6 +22,8 @@
#include "libc/runtime/syslib.internal.h"
#include "libc/thread/thread.h"
void sys_sched_yield(void);
/**
* Yields current thread's remaining timeslice to operating system.
*
@@ -31,12 +33,15 @@ int pthread_yield_np(void) {
if (IsXnuSilicon()) {
__syslib->__pthread_yield_np();
} else if (IsOpenbsd()) {
pthread_pause_np(); // sched_yield() is punishingly slow on OpenBSD
// sched_yield() is punishingly slow on OpenBSD
// it's ruinously slow it'll destroy everything
pthread_pause_np();
} else {
sched_yield();
sys_sched_yield();
}
return 0;
}
__weak_reference(pthread_yield_np, thrd_yield);
__weak_reference(pthread_yield_np, sched_yield);
__weak_reference(pthread_yield_np, pthread_yield);
@@ -19,12 +19,11 @@
#include "libc/dce.h"
#include "libc/sysv/consts/nr.h"
#include "libc/macros.internal.h"
.privileged
// Relinquishes scheduled quantum.
//
// @return 0 on success, or -1 w/ errno
sched_yield:
sys_sched_yield:
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
@@ -107,5 +106,5 @@ sched_yield:
#else
#error "arch unsupported"
#endif
.endfn sched_yield,globl
.endfn sys_sched_yield,globl
.previous
-1
View File
@@ -1 +0,0 @@
#include "third_party/libcxx/__threading_support"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/amxcomplexintrin.internal.h"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/amxfp16intrin.internal.h"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/avxifmaintrin.internal.h"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/avxneconvertintrin.internal.h"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/avxvnniint16intrin.internal.h"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/avxvnniint8intrin.internal.h"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/bmmintrin.internal.h"
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/cmpccxaddintrin.internal.h"
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef _CTYPE_H
#define _CTYPE_H
#include "libc/str/str.h"
#include "libc/ctype.h"
#endif /* _CTYPE_H */
+1
View File
@@ -0,0 +1 @@
#include "third_party/intel/prfchiintrin.internal.h"
Loaded 100 of 1949 files, more files were not shown because too many files have changed in this diff. Show more