mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-26 07:49:05 +00:00
Improve AARCH64 execution
This change fixes bugs in the APE loader. The execve() unit tests are now enabled for MODE=aarch64. See the README for how you need to have binfmt_misc configured with Qemu to run them. Apple Silicon bugs have been fixed too, e.g. tkill() now works.
This commit is contained in:
parent
1965d7488e
commit
77a7873057
31 changed files with 599 additions and 195 deletions
MakefileREADME.md
ape
bin
libc
calls
execve-nt.greg.cexecve-sysv.cexecve.cexecve.internal.hfexecve.cgetprogramexecutablename.greg.cisapemagic.cntspawn.ctkill.c
intrin
runtime
sysv
testlib
thread
test/libc
tool/build
2
Makefile
2
Makefile
|
@ -112,6 +112,8 @@ endif
|
||||||
/etc/hosts \
|
/etc/hosts \
|
||||||
~/.runit.psk \
|
~/.runit.psk \
|
||||||
/proc/self/status \
|
/proc/self/status \
|
||||||
|
rx:/usr/bin/qemu-aarch64 \
|
||||||
|
rx:o/third_party/qemu/qemu-aarch64 \
|
||||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||||
|
|
||||||
PKGS =
|
PKGS =
|
||||||
|
|
211
README.md
211
README.md
|
@ -128,25 +128,7 @@ cosmocc -Os -o hello2.com hello2.c
|
||||||
## ARM
|
## ARM
|
||||||
|
|
||||||
Cosmo supports cross-compiling binaries for machines with ARM
|
Cosmo supports cross-compiling binaries for machines with ARM
|
||||||
microprocessors. There are two options available for doing this.
|
microprocessors. For example:
|
||||||
|
|
||||||
The first option is to embed the [blink virtual
|
|
||||||
machine](https://github.com/jart/blink) by adding the following to the
|
|
||||||
top of your main.c file:
|
|
||||||
|
|
||||||
```c
|
|
||||||
__static_yoink("blink_linux_aarch64"); // for raspberry pi
|
|
||||||
__static_yoink("blink_xnu_aarch64"); // is apple silicon
|
|
||||||
```
|
|
||||||
|
|
||||||
The benefit is you'll have single file executables that'll run on both
|
|
||||||
x86_64 and arm64 platforms. The tradeoff is Blink's JIT is slower than
|
|
||||||
running natively, but tends to go fast enough, unless you're doing
|
|
||||||
scientific computing (e.g. running LLMs with
|
|
||||||
`o//third_party/ggml/llama.com`).
|
|
||||||
|
|
||||||
Therefore, the second option is to cross compile aarch64 executables,
|
|
||||||
by using build modes like the following:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make -j8 m=aarch64 o/aarch64/third_party/ggml/llama.com
|
make -j8 m=aarch64 o/aarch64/third_party/ggml/llama.com
|
||||||
|
@ -175,7 +157,196 @@ You can run your ELF AARCH64 executable on Apple Silicon as follows:
|
||||||
ape ./llama.com
|
ape ./llama.com
|
||||||
```
|
```
|
||||||
|
|
||||||
## Source Builds
|
If you want to run the `MODE=aarch64` unit tests, you need to have
|
||||||
|
qemu-aarch64 installed as a binfmt_misc interpreter. It needs to be a
|
||||||
|
static binary if you want it to work with Landlock Make's security. You
|
||||||
|
can use the build included in our `third_party/qemu/` folder.
|
||||||
|
|
||||||
|
```
|
||||||
|
doas cp o/third_party/qemu/qemu-aarch64 /usr/bin/qemu-aarch64
|
||||||
|
doas sh -c "echo ':qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-aarch64:CF' > /proc/sys/fs/binfmt_misc/register"
|
||||||
|
make -j8 m=aarch64
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that the qemu-aarch64 binfmt_misc interpreter installation
|
||||||
|
process is *essential* for being able to use the `aarch64-unknown-cosmo`
|
||||||
|
toolchain to build fat APE binaries on your x86-64 machine.
|
||||||
|
|
||||||
|
## AMD64 + ARM64 fat APE binaries
|
||||||
|
|
||||||
|
If you've setup the qemu binfmt_misc interpreter, then you can can use
|
||||||
|
cosmo's toolchains to build fat ape binaries. It works by compiling your
|
||||||
|
program twice, so you can have a native build for both architectures in
|
||||||
|
the same file. The two programs are merged together by apelink.com which
|
||||||
|
also embeds multiple copies of APE loader and multiple symbols tables.
|
||||||
|
|
||||||
|
The easiest way to build fat APE is using `fatcosmocc`. This compiler
|
||||||
|
works by creating a concomitant `.aarch64/foo.o` for every `foo.o` you
|
||||||
|
compile. The only exception is the C preprocessor mode, which actually
|
||||||
|
runs x86-64 GCC except with macros like `__x86_64__` undefined.
|
||||||
|
|
||||||
|
This toolchain works great for C projects that are written in a portable
|
||||||
|
way and don't produce architecturue-specific artifacts. One example of a
|
||||||
|
large project that can be easily built is GNU coreutils.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd coreutils
|
||||||
|
fatcosmocc --update ||exit
|
||||||
|
./configure CC=fatcosmocc \
|
||||||
|
AR=fatcosmoar \
|
||||||
|
INSTALL=$(command -v fatcosmoinstall) \
|
||||||
|
--prefix=/opt/cosmos \
|
||||||
|
--disable-nls \
|
||||||
|
--disable-dependency-tracking \
|
||||||
|
--disable-silent-rules
|
||||||
|
make -j8
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll then have a bunch of files like `src/ls` which are fat ape
|
||||||
|
binaries. If you want to run them on Windows, then you simply need to
|
||||||
|
rename the file so that it has the `.com` suffix. Better yet, consider
|
||||||
|
making that a symlink (a.k.a. reparse point). The biggest gotcha with
|
||||||
|
`fatcosmocc` though is ensuring builds don't strip binaries. For
|
||||||
|
example, Linux's `install -s` command actually understands Windows'
|
||||||
|
Portable Executable format well enough to remove the MS-DOS stub, which
|
||||||
|
is where the APE shell script is stored. You need to ensure that
|
||||||
|
`fatcosmoinstall` is used instead. Especially if your project needs to
|
||||||
|
install the libraries built by `fatacosmoar` into `/opt/cosmos`.
|
||||||
|
|
||||||
|
## Advanced Fat APE Builds
|
||||||
|
|
||||||
|
Once you get seriously involved in creating fat APE builds of software
|
||||||
|
you're going to eventually outgrow `fatcosmocc`. One example is Emacs
|
||||||
|
which is trickier to build, because it produces architecture-specific
|
||||||
|
files, and it also depends on shared files, e.g. zoneinfo. Since we like
|
||||||
|
having everything in a neat little single-file executable container that
|
||||||
|
doesn't need an "installation wizard", this tutorial will explain how we
|
||||||
|
manage to accomplish that.
|
||||||
|
|
||||||
|
What you're going to do is, instead of using `fatcosmocc`, you're going
|
||||||
|
to use both the `x86_64-unknown-cosmo-cc` and `aarch64-unknown-cosmo-cc`
|
||||||
|
toolchains independently, and then run `apelink` and `zip` to manually
|
||||||
|
build the final files. But there's a few tricks to learn first.
|
||||||
|
|
||||||
|
The first trick is to create a symlink on your system called `/zip`.
|
||||||
|
Cosmopolitan Libc normally uses that as a synthetic folder that lets you
|
||||||
|
access the assets in your zip executable. But since that's a read-only
|
||||||
|
file system, your build system should use the normal one.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
doas ln -sf /opt/cosmos /zip
|
||||||
|
```
|
||||||
|
|
||||||
|
Now create a file named `rebuild-fat.sh` which runs the build twice:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/sh
|
||||||
|
set -ex
|
||||||
|
export MODE=aarch64
|
||||||
|
export COSMOS=/opt/cosmos/aarch64
|
||||||
|
rebuild-cosmos.sh aarch64
|
||||||
|
export MODE=
|
||||||
|
export COSMOS=/opt/cosmos/x86_64
|
||||||
|
rebuild-cosmos.sh x86_64
|
||||||
|
wall.com 'finished building'
|
||||||
|
```
|
||||||
|
|
||||||
|
Then create a second file `rebuild-cosmos.sh` which runs your build:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
ARCH=${1:-x86_64}
|
||||||
|
export COSMO=${COSMO:-/opt/cosmo}
|
||||||
|
export COSMOS=${COSMOS:-/opt/cosmos/$ARCH}
|
||||||
|
export AS=$(command -v $ARCH-unknown-cosmo-as) || exit
|
||||||
|
export CC=$(command -v $ARCH-unknown-cosmo-cc) || exit
|
||||||
|
export CXX=$(command -v $ARCH-unknown-cosmo-c++) || exit
|
||||||
|
export AR=$(command -v $ARCH-unknown-cosmo-ar) || exit
|
||||||
|
export STRIP=$(command -v $ARCH-unknown-cosmo-strip) || exit
|
||||||
|
export INSTALL=$(command -v $ARCH-unknown-cosmo-install) || exit
|
||||||
|
export OBJCOPY=$(command -v $ARCH-unknown-cosmo-objcopy) || exit
|
||||||
|
export OBJDUMP=$(command -v $ARCH-unknown-cosmo-objdump) || exit
|
||||||
|
export ADDR2LINE=$(command -v $ARCH-unknown-cosmo-addr2line) || exit
|
||||||
|
|
||||||
|
$CC --update
|
||||||
|
|
||||||
|
export COSMOPOLITAN_DISABLE_ZIPOS=1
|
||||||
|
|
||||||
|
cd ~/vendor/zlib
|
||||||
|
./configure --prefix=$COSMOS --static
|
||||||
|
make clean
|
||||||
|
make -j
|
||||||
|
make install
|
||||||
|
|
||||||
|
cd ~/vendor/ncurses-6.4
|
||||||
|
./configure --prefix=$COSMOS --sysconfdir=/zip --datarootdir=/zip/share --exec-prefix=/zip/$ARCH --disable-shared
|
||||||
|
make clean
|
||||||
|
make -j
|
||||||
|
make install
|
||||||
|
|
||||||
|
cd ~/vendor/readline-8.2
|
||||||
|
./configure --prefix=$COSMOS --sysconfdir=/zip --datarootdir=/zip/share --exec-prefix=/zip/$ARCH --disable-shared
|
||||||
|
make uninstall || true
|
||||||
|
make clean
|
||||||
|
make -j
|
||||||
|
make install
|
||||||
|
|
||||||
|
# NOTES:
|
||||||
|
# 1. You'll need to patch enum { FOO = x } that fails to build into a #define FOO
|
||||||
|
# 2. You'll need to patch configure.ac so it DOES NOT define USABLE_FIONREAD to 1
|
||||||
|
# 2. You'll need to patch configure.ac so it DOES NOT define INTERRUPT_INPUT to 1
|
||||||
|
cd ~/vendor/emacs-28.2
|
||||||
|
./configure --prefix=$COSMOS --sysconfdir=/zip --datarootdir=/zip/share --exec-prefix=/zip/$ARCH \
|
||||||
|
--without-x --with-threads --without-gnutls --disable-silent-rules --with-file-notification=no
|
||||||
|
make uninstall || true
|
||||||
|
make clean
|
||||||
|
make -j
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you've completed this build process, you'll have the ELF files
|
||||||
|
`/opt/cosmos/x86_64/bin/emacs` and `/opt/cosmos/aarch64/bin/emacs`. Your
|
||||||
|
next move is to combine them into a single pristine `emacs.com` file.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /zip
|
||||||
|
COSMO=${COSMO:-/opt/cosmo}
|
||||||
|
mkdir -p /opt/cosmos/bin
|
||||||
|
apelink \
|
||||||
|
-o /opt/cosmos/bin/emacs.com \
|
||||||
|
-l "$COSMO/o//ape/ape.elf" \
|
||||||
|
-l "$COSMO/o/aarch64/ape/ape.elf" \
|
||||||
|
-M "$COSMO/ape/ape-m1.c" \
|
||||||
|
/opt/cosmos/x86_64/bin/emacs \
|
||||||
|
/opt/cosmos/aarch64/bin/emacs
|
||||||
|
cd /zip
|
||||||
|
zip -r /opt/cosmos/bin/emacs.com \
|
||||||
|
aarch64/libexec \
|
||||||
|
x86_64/libexec \
|
||||||
|
share/terminfo \
|
||||||
|
$(find share/emacs -type f |
|
||||||
|
grep -v '\.el.gz$' |
|
||||||
|
grep -v refcards |
|
||||||
|
grep -v images)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now scp your `emacs.com` build to seven operating systems for
|
||||||
|
two distinct kinds of microprocessors without any dependencies. All the
|
||||||
|
LISP, zoneinfo, and termcap files it needs are stored inside the ZIP
|
||||||
|
structure of the binary, which has performance that's equivalent to the
|
||||||
|
Linux filesystem (even though it decompresses artifacts on the fly!) For
|
||||||
|
this reason, you might actually find that fat APE Emacs goes faster if
|
||||||
|
you're using an operating system like Windows where files are go slow.
|
||||||
|
|
||||||
|
If you like to use Vim instead of Emacs, then you can build that too.
|
||||||
|
However Vim's build system makes it a bit harder, since it's configured
|
||||||
|
to always strip binaries. The `apelink` program needs the symbol tables
|
||||||
|
to still be there when it creates the fat version. Otherwise tools like
|
||||||
|
`--ftrace` won't work.
|
||||||
|
|
||||||
|
## Monolithic Source Builds
|
||||||
|
|
||||||
Cosmopolitan can be compiled from source on any Linux distro. First, you
|
Cosmopolitan can be compiled from source on any Linux distro. First, you
|
||||||
need to download or clone the repository.
|
need to download or clone the repository.
|
||||||
|
|
28
ape/ape-m1.c
28
ape/ape-m1.c
|
@ -151,6 +151,7 @@ union ElfPhdrBuf {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathSearcher {
|
struct PathSearcher {
|
||||||
|
int literally;
|
||||||
unsigned long namelen;
|
unsigned long namelen;
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *syspath;
|
const char *syspath;
|
||||||
|
@ -377,10 +378,27 @@ static char SearchPath(struct PathSearcher *ps, const char *suffix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
||||||
if (MemChr(ps->name, '/', ps->namelen)) {
|
|
||||||
ps->path[0] = 0;
|
ps->path[0] = 0;
|
||||||
|
|
||||||
|
/* paths are always 100% taken literally when a slash exists
|
||||||
|
$ ape foo/bar.com arg1 arg2 */
|
||||||
|
if (MemChr(ps->name, '/', ps->namelen)) {
|
||||||
return AccessCommand(ps, suffix, 0);
|
return AccessCommand(ps, suffix, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* we don't run files in the current directory
|
||||||
|
$ ape foo.com arg1 arg2
|
||||||
|
unless $PATH has an empty string entry, e.g.
|
||||||
|
$ expert PATH=":/bin"
|
||||||
|
$ ape foo.com arg1 arg2
|
||||||
|
however we will execute this
|
||||||
|
$ ape - foo.com foo.com arg1 arg2
|
||||||
|
because cosmo's execve needs it */
|
||||||
|
if (ps->literally && AccessCommand(ps, suffix, 0)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise search for name on $PATH */
|
||||||
return SearchPath(ps, suffix);
|
return SearchPath(ps, suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,7 +843,7 @@ int main(int argc, char **argv, char **envp) {
|
||||||
auxv = (long *)(envp + i + 1);
|
auxv = (long *)(envp + i + 1);
|
||||||
|
|
||||||
/* interpret command line arguments */
|
/* interpret command line arguments */
|
||||||
if (argc >= 3 && !StrCmp(argv[1], "-")) {
|
if ((M->ps.literally = argc >= 3 && !StrCmp(argv[1], "-"))) {
|
||||||
/* if the first argument is a hyphen then we give the user the
|
/* if the first argument is a hyphen then we give the user the
|
||||||
power to change argv[0] or omit it entirely. most operating
|
power to change argv[0] or omit it entirely. most operating
|
||||||
systems don't permit the omission of argv[0] but we do, b/c
|
systems don't permit the omission of argv[0] but we do, b/c
|
||||||
|
@ -836,7 +854,7 @@ int main(int argc, char **argv, char **envp) {
|
||||||
} else if (argc < 2) {
|
} else if (argc < 2) {
|
||||||
Emit("usage: ape PROG [ARGV1,ARGV2,...]\n"
|
Emit("usage: ape PROG [ARGV1,ARGV2,...]\n"
|
||||||
" ape - PROG [ARGV0,ARGV1,...]\n"
|
" ape - PROG [ARGV0,ARGV1,...]\n"
|
||||||
"actually portable executable loader silicon 1.7\n"
|
"actually portable executable loader silicon 1.8\n"
|
||||||
"copyright 2023 justine alexandra roberts tunney\n"
|
"copyright 2023 justine alexandra roberts tunney\n"
|
||||||
"https://justine.lol/ape.html\n");
|
"https://justine.lol/ape.html\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
|
@ -881,8 +899,8 @@ int main(int argc, char **argv, char **envp) {
|
||||||
pe = ebuf->buf + rc;
|
pe = ebuf->buf + rc;
|
||||||
|
|
||||||
/* resolve argv[0] to reflect path search */
|
/* resolve argv[0] to reflect path search */
|
||||||
if ((argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) ||
|
if (argc > 0 && ((*prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) ||
|
||||||
!StrCmp(BaseName(prog), argv[0])) {
|
!StrCmp(BaseName(prog), argv[0]))) {
|
||||||
argv[0] = exe;
|
argv[0] = exe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
ape/loader.c
28
ape/loader.c
|
@ -208,6 +208,7 @@ union ElfPhdrBuf {
|
||||||
|
|
||||||
struct PathSearcher {
|
struct PathSearcher {
|
||||||
int os;
|
int os;
|
||||||
|
int literally;
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *syspath;
|
const char *syspath;
|
||||||
unsigned long namelen;
|
unsigned long namelen;
|
||||||
|
@ -588,10 +589,27 @@ static char SearchPath(struct PathSearcher *ps, const char *suffix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
||||||
if (MemChr(ps->name, '/', ps->namelen)) {
|
|
||||||
ps->path[0] = 0;
|
ps->path[0] = 0;
|
||||||
|
|
||||||
|
/* paths are always 100% taken literally when a slash exists
|
||||||
|
$ ape foo/bar.com arg1 arg2 */
|
||||||
|
if (MemChr(ps->name, '/', ps->namelen)) {
|
||||||
return AccessCommand(ps, suffix, 0);
|
return AccessCommand(ps, suffix, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* we don't run files in the current directory
|
||||||
|
$ ape foo.com arg1 arg2
|
||||||
|
unless $PATH has an empty string entry, e.g.
|
||||||
|
$ expert PATH=":/bin"
|
||||||
|
$ ape foo.com arg1 arg2
|
||||||
|
however we will execute this
|
||||||
|
$ ape - foo.com foo.com arg1 arg2
|
||||||
|
because cosmo's execve needs it */
|
||||||
|
if (ps->literally && AccessCommand(ps, suffix, 0)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise search for name on $PATH */
|
||||||
return SearchPath(ps, suffix);
|
return SearchPath(ps, suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,6 +933,7 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
|
||||||
char dl) {
|
char dl) {
|
||||||
int rc, n;
|
int rc, n;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
char literally;
|
||||||
const char *ape;
|
const char *ape;
|
||||||
int c, fd, os, argc;
|
int c, fd, os, argc;
|
||||||
struct ApeLoader *M;
|
struct ApeLoader *M;
|
||||||
|
@ -989,7 +1008,7 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we can load via shell, shebang, or binfmt_misc */
|
/* we can load via shell, shebang, or binfmt_misc */
|
||||||
if (argc >= 3 && !StrCmp(argv[1], "-")) {
|
if ((literally = argc >= 3 && !StrCmp(argv[1], "-"))) {
|
||||||
/* if the first argument is a hyphen then we give the user the
|
/* if the first argument is a hyphen then we give the user the
|
||||||
power to change argv[0] or omit it entirely. most operating
|
power to change argv[0] or omit it entirely. most operating
|
||||||
systems don't permit the omission of argv[0] but we do, b/c
|
systems don't permit the omission of argv[0] but we do, b/c
|
||||||
|
@ -1009,6 +1028,7 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
|
||||||
/* allocate loader memory in program's arg block */
|
/* allocate loader memory in program's arg block */
|
||||||
n = sizeof(struct ApeLoader);
|
n = sizeof(struct ApeLoader);
|
||||||
M = (struct ApeLoader *)__builtin_alloca(n);
|
M = (struct ApeLoader *)__builtin_alloca(n);
|
||||||
|
M->ps.literally = literally;
|
||||||
|
|
||||||
/* create new bottom of stack for spawned program
|
/* create new bottom of stack for spawned program
|
||||||
system v abi aligns this on a 16-byte boundary
|
system v abi aligns this on a 16-byte boundary
|
||||||
|
@ -1045,8 +1065,8 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
|
||||||
pe = ebuf->buf + rc;
|
pe = ebuf->buf + rc;
|
||||||
|
|
||||||
/* change argv[0] to resolved path if it's ambiguous */
|
/* change argv[0] to resolved path if it's ambiguous */
|
||||||
if ((argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) ||
|
if (argc > 0 && ((*prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) ||
|
||||||
!StrCmp(BaseName(prog), argv[0])) {
|
!StrCmp(BaseName(prog), argv[0]))) {
|
||||||
argv[0] = exe;
|
argv[0] = exe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ for x in .ape \
|
||||||
.ape-1.5 \
|
.ape-1.5 \
|
||||||
.ape-1.6 \
|
.ape-1.6 \
|
||||||
.ape-1.7 \
|
.ape-1.7 \
|
||||||
|
.ape-1.8 \
|
||||||
.ape-blink-0.9.2 \
|
.ape-blink-0.9.2 \
|
||||||
.ape-blink-1.0.0; do
|
.ape-blink-1.0.0; do
|
||||||
rm -f \
|
rm -f \
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
#ifdef __x86_64__
|
||||||
|
|
||||||
#define keywords textwindows dontasan dontubsan dontinstrument
|
#define keywords textwindows dontasan dontubsan dontinstrument
|
||||||
|
|
||||||
|
@ -275,3 +276,5 @@ static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) {
|
||||||
__imp_ExitProcess(dwExitCode);
|
__imp_ExitProcess(dwExitCode);
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -17,15 +17,21 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "ape/ape.h"
|
#include "ape/ape.h"
|
||||||
|
#include "libc/atomic.h"
|
||||||
#include "libc/calls/blockcancel.internal.h"
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/calls/blocksigs.internal.h"
|
#include "libc/calls/blocksigs.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/execve-sysv.internal.h"
|
#include "libc/calls/cp.internal.h"
|
||||||
|
#include "libc/calls/execve.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
|
#include "libc/cosmo.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/fmt/magnumstrs.internal.h"
|
||||||
#include "libc/intrin/bits.h"
|
#include "libc/intrin/bits.h"
|
||||||
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/intrin/safemacros.internal.h"
|
#include "libc/intrin/safemacros.internal.h"
|
||||||
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/mem/alloca.h"
|
#include "libc/mem/alloca.h"
|
||||||
#include "libc/paths.h"
|
#include "libc/paths.h"
|
||||||
|
@ -36,77 +42,100 @@
|
||||||
#include "libc/sysv/consts/ok.h"
|
#include "libc/sysv/consts/ok.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
static bool CanExecute(const char *path) {
|
#define ELIBBAD_LINUX 80
|
||||||
return !sys_faccessat(AT_FDCWD, path, X_OK, 0);
|
#define EBADEXEC_XNU 85
|
||||||
}
|
#define EBADARCH_XNU 86
|
||||||
|
|
||||||
bool IsAPEMagic(char buf[8]) {
|
static struct {
|
||||||
return READ64LE(buf) == READ64LE("MZqFpD='") ||
|
atomic_uint once;
|
||||||
READ64LE(buf) == READ64LE("JTqFpD='");
|
const char *home;
|
||||||
}
|
const char *tmpdir;
|
||||||
|
} g_execve;
|
||||||
|
|
||||||
static bool IsApeBinary(const char *path) {
|
static bool IsApeFile(const char *path) {
|
||||||
|
if (!endswith(path, ".com")) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
bool res = false;
|
||||||
|
BLOCK_CANCELLATIONS;
|
||||||
|
BEGIN_CANCELLATION_POINT;
|
||||||
int fd;
|
int fd;
|
||||||
char buf[8];
|
char buf[8];
|
||||||
bool res = false;
|
int flags = O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC;
|
||||||
// TODO(jart): Should we block signals too?
|
if ((fd = sys_openat(AT_FDCWD, path, flags, 0)) != -1) {
|
||||||
BLOCK_CANCELLATIONS;
|
res = sys_pread(fd, buf, 8, 0, 0) == 8 && IsApeLoadable(buf);
|
||||||
if ((fd = sys_openat(AT_FDCWD, path, O_RDONLY, 0)) != -1) {
|
|
||||||
res = sys_read(fd, buf, 8) == 8 && IsAPEMagic(buf);
|
|
||||||
sys_close(fd);
|
sys_close(fd);
|
||||||
}
|
}
|
||||||
|
END_CANCELLATION_POINT;
|
||||||
ALLOW_CANCELLATIONS;
|
ALLOW_CANCELLATIONS;
|
||||||
return res;
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) {
|
static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) {
|
||||||
size_t n, m;
|
size_t n, m;
|
||||||
|
if (a && *a) {
|
||||||
n = strlen(a);
|
n = strlen(a);
|
||||||
m = strlen(b);
|
m = strlen(b);
|
||||||
if (n + 1 + m + 1 < PATH_MAX) {
|
if (n + m + 1 < PATH_MAX) {
|
||||||
stpcpy(stpcpy(stpcpy(buf, a), "/"), b);
|
stpcpy(stpcpy(buf, a), b);
|
||||||
return buf;
|
return buf;
|
||||||
} else {
|
}
|
||||||
return "";
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RetryExecve(const char *prog, char **argv, char *const *envp) {
|
||||||
|
if ((argv[0] = (char *)prog)) {
|
||||||
|
STRACE("execve(%#s, %s) due to %s", prog, DescribeStringList(argv),
|
||||||
|
_strerrno(errno));
|
||||||
|
__sys_execve(prog, argv, envp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SetupExecve(void) {
|
||||||
|
g_execve.home = getenv("HOME");
|
||||||
|
g_execve.tmpdir = getenv("TMPDIR");
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__constructor__)) static void InitExecve(void) {
|
||||||
|
cosmo_once(&g_execve.once, SetupExecve);
|
||||||
|
}
|
||||||
|
|
||||||
int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
|
int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||||
size_t i;
|
|
||||||
int e, rc;
|
// try kernel
|
||||||
char *buf;
|
// this also checks execute bit
|
||||||
char **shargs;
|
|
||||||
const char *ape;
|
|
||||||
e = errno;
|
|
||||||
__sys_execve(prog, argv, envp);
|
__sys_execve(prog, argv, envp);
|
||||||
if (errno == ENOEXEC) {
|
if (!(errno == ENOEXEC || (IsLinux() && errno == ELIBBAD_LINUX))) {
|
||||||
for (i = 0; argv[i];) ++i;
|
return -1;
|
||||||
buf = alloca(PATH_MAX);
|
}
|
||||||
shargs = alloca((i + 4) * sizeof(char *));
|
|
||||||
if (IsApeBinary(prog) &&
|
// allocate memory
|
||||||
(CanExecute((ape = "/usr/bin/ape")) ||
|
int argc;
|
||||||
CanExecute((ape = Join(firstnonnull(getenv("TMPDIR"),
|
for (argc = 0; argv[argc];) ++argc;
|
||||||
firstnonnull(getenv("HOME"), ".")),
|
char **shargs = alloca((argc + 4) * sizeof(char *));
|
||||||
".ape-" APE_VERSION_STR, buf))) ||
|
|
||||||
CanExecute((ape = Join(firstnonnull(getenv("HOME"), "."),
|
// try ape loader
|
||||||
".ape-" APE_VERSION_STR, buf))))) {
|
if (IsApeFile(prog)) {
|
||||||
shargs[0] = (char *)ape;
|
|
||||||
shargs[1] = (char *)"-";
|
shargs[1] = (char *)"-";
|
||||||
shargs[2] = (char *)prog;
|
shargs[2] = (char *)prog;
|
||||||
memcpy(shargs + 3, argv, (i + 1) * sizeof(char *));
|
memcpy(shargs + 3, argv, (argc + 1) * sizeof(char *));
|
||||||
errno = e;
|
RetryExecve("/usr/bin/ape", shargs, envp);
|
||||||
rc = __sys_execve(shargs[0], shargs, envp);
|
char *buf = alloca(PATH_MAX);
|
||||||
} else if (CanExecute(prog)) {
|
const char *name = "/.ape-" APE_VERSION_STR;
|
||||||
|
InitExecve();
|
||||||
|
RetryExecve(Join(g_execve.tmpdir, name, buf), shargs, envp);
|
||||||
|
RetryExecve(Join(g_execve.home, name, buf), shargs, envp);
|
||||||
|
RetryExecve(Join(".", name, buf), shargs, envp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try bourne shell
|
||||||
shargs[0] = _PATH_BSHELL;
|
shargs[0] = _PATH_BSHELL;
|
||||||
shargs[1] = (char *)prog;
|
shargs[1] = (char *)prog;
|
||||||
memcpy(shargs + 2, argv + 1, i * sizeof(char *));
|
memcpy(shargs + 2, argv + 1, argc * sizeof(char *));
|
||||||
errno = e;
|
RetryExecve(shargs[0], shargs, envp);
|
||||||
rc = __sys_execve(shargs[0], shargs, envp);
|
|
||||||
} else {
|
enoexec();
|
||||||
rc = enoexec();
|
return -1;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rc = -1;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||||
!__asan_is_valid_strlist(envp)))) {
|
!__asan_is_valid_strlist(envp)))) {
|
||||||
rc = efault();
|
rc = efault();
|
||||||
} else {
|
} else {
|
||||||
STRACE("execve(%#s, %s, %s) → ...", prog, DescribeStringList(argv),
|
STRACE("execve(%#s, %s, %s)", prog, DescribeStringList(argv),
|
||||||
DescribeStringList(envp));
|
DescribeStringList(envp));
|
||||||
rc = 0;
|
rc = 0;
|
||||||
if (IsLinux() && __execpromises && _weaken(sys_pledge_linux)) {
|
if (IsLinux() && __execpromises && _weaken(sys_pledge_linux)) {
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
bool IsAPEMagic(char[8]);
|
void __execve_lock(void);
|
||||||
|
void __execve_unlock(void);
|
||||||
|
bool IsApeLoadable(char[8]);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
|
@ -21,7 +21,7 @@
|
||||||
#include "libc/calls/blocksigs.internal.h"
|
#include "libc/calls/blocksigs.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/cp.internal.h"
|
#include "libc/calls/cp.internal.h"
|
||||||
#include "libc/calls/execve-sysv.internal.h"
|
#include "libc/calls/execve.internal.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/struct/stat.internal.h"
|
#include "libc/calls/struct/stat.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
#include "libc/fmt/itoa.h"
|
#include "libc/fmt/itoa.h"
|
||||||
#include "libc/intrin/asan.internal.h"
|
#include "libc/intrin/asan.internal.h"
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/safemacros.internal.h"
|
#include "libc/intrin/safemacros.internal.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
|
@ -46,7 +47,7 @@
|
||||||
|
|
||||||
static bool IsAPEFd(const int fd) {
|
static bool IsAPEFd(const int fd) {
|
||||||
char buf[8];
|
char buf[8];
|
||||||
return (sys_pread(fd, buf, 8, 0, 0) == 8) && IsAPEMagic(buf);
|
return (sys_pread(fd, buf, 8, 0, 0) == 8) && IsApeLoadable(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) {
|
static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) {
|
||||||
|
@ -141,7 +142,7 @@ static int fd_to_mem_fd(const int infd, char *path) {
|
||||||
ssize_t readRc;
|
ssize_t readRc;
|
||||||
readRc = pread(infd, space, st.st_size, 0);
|
readRc = pread(infd, space, st.st_size, 0);
|
||||||
bool success = readRc != -1;
|
bool success = readRc != -1;
|
||||||
if (success && (st.st_size > 8) && IsAPEMagic(space)) {
|
if (success && (st.st_size > 8) && IsApeLoadable(space)) {
|
||||||
int flags = fcntl(fd, F_GETFD);
|
int flags = fcntl(fd, F_GETFD);
|
||||||
if ((success = (flags != -1) &&
|
if ((success = (flags != -1) &&
|
||||||
(fcntl(fd, F_SETFD, flags & (~FD_CLOEXEC)) != -1) &&
|
(fcntl(fd, F_SETFD, flags & (~FD_CLOEXEC)) != -1) &&
|
||||||
|
|
|
@ -93,9 +93,9 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
|
||||||
|
|
||||||
// if argv[0] exists then turn it into an absolute path. we also try
|
// if argv[0] exists then turn it into an absolute path. we also try
|
||||||
// adding a .com suffix since the ape auto-appends it when resolving
|
// adding a .com suffix since the ape auto-appends it when resolving
|
||||||
if (((q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) ||
|
if ((q = __argv[0]) && ((!sys_faccessat(AT_FDCWD, q, F_OK, 0)) ||
|
||||||
((q = StrCat(u.path, __argv[0], ".com")) &&
|
((q = StrCat(u.path, __argv[0], ".com")) &&
|
||||||
!sys_faccessat(AT_FDCWD, q, F_OK, 0))) {
|
!sys_faccessat(AT_FDCWD, q, F_OK, 0)))) {
|
||||||
if (*q != '/') {
|
if (*q != '/') {
|
||||||
if (q[0] == '.' && q[1] == '/') {
|
if (q[0] == '.' && q[1] == '/') {
|
||||||
q += 2;
|
q += 2;
|
||||||
|
|
29
libc/calls/isapemagic.c
Normal file
29
libc/calls/isapemagic.c
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 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/calls/calls.h"
|
||||||
|
#include "libc/intrin/bits.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if executable image is supported by APE Loader.
|
||||||
|
*/
|
||||||
|
bool IsApeLoadable(char buf[8]) {
|
||||||
|
return READ32LE(buf) == READ32LE("\177ELF") ||
|
||||||
|
READ64LE(buf) == READ64LE("MZqFpD='") ||
|
||||||
|
READ64LE(buf) == READ64LE("JTqFpD='");
|
||||||
|
}
|
|
@ -34,6 +34,7 @@
|
||||||
#include "libc/nt/struct/securityattributes.h"
|
#include "libc/nt/struct/securityattributes.h"
|
||||||
#include "libc/nt/struct/startupinfo.h"
|
#include "libc/nt/struct/startupinfo.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
#ifdef __x86_64__
|
||||||
|
|
||||||
struct SpawnBlock {
|
struct SpawnBlock {
|
||||||
union {
|
union {
|
||||||
|
@ -108,3 +109,5 @@ textwindows int ntspawn(
|
||||||
if (handle) CloseHandle(handle);
|
if (handle) CloseHandle(handle);
|
||||||
return __fix_enotdir(rc, prog16);
|
return __fix_enotdir(rc, prog16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/struct/context.h"
|
#include "libc/nt/struct/context.h"
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
|
#include "libc/runtime/syslib.internal.h"
|
||||||
#include "libc/sysv/consts/sicode.h"
|
#include "libc/sysv/consts/sicode.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
@ -97,10 +98,17 @@ static dontinline textwindows int __tkill_nt(int tid, int sig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __tkill_m1(int tid, int sig, struct CosmoTib *tib) {
|
||||||
|
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||||
|
return __syslib->pthread_kill(pt->next, sig);
|
||||||
|
}
|
||||||
|
|
||||||
// OpenBSD has an optional `tib` parameter for extra safety.
|
// OpenBSD has an optional `tib` parameter for extra safety.
|
||||||
int __tkill(int tid, int sig, void *tib) {
|
int __tkill(int tid, int sig, void *tib) {
|
||||||
int rc;
|
int rc;
|
||||||
if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
|
if (IsXnuSilicon()) {
|
||||||
|
return __tkill_m1(tid, sig, tib);
|
||||||
|
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
|
||||||
rc = sys_tkill(tid, sig, tib);
|
rc = sys_tkill(tid, sig, tib);
|
||||||
} else if (IsWindows()) {
|
} else if (IsWindows()) {
|
||||||
rc = __tkill_nt(tid, sig, tib);
|
rc = __tkill_nt(tid, sig, tib);
|
||||||
|
|
|
@ -406,11 +406,9 @@ static bool __asan_is_mapped(int x) {
|
||||||
// xxx: we can't lock because no reentrant locks yet
|
// xxx: we can't lock because no reentrant locks yet
|
||||||
int i;
|
int i;
|
||||||
bool res;
|
bool res;
|
||||||
struct MemoryIntervals *m;
|
|
||||||
__mmi_lock();
|
__mmi_lock();
|
||||||
m = _weaken(_mmi);
|
i = __find_memory(&_mmi, x);
|
||||||
i = __find_memory(m, x);
|
res = i < _mmi.i && x >= _mmi.p[i].x;
|
||||||
res = i < m->i && x >= m->p[i].x;
|
|
||||||
__mmi_unlock();
|
__mmi_unlock();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -902,7 +900,7 @@ static __wur __asan_die_f *__asan_report(const void *addr, int size,
|
||||||
p = __asan_format_section(p, _etext, _edata, ".data", addr);
|
p = __asan_format_section(p, _etext, _edata, ".data", addr);
|
||||||
p = __asan_format_section(p, _end, _edata, ".bss", addr);
|
p = __asan_format_section(p, _end, _edata, ".bss", addr);
|
||||||
__mmi_lock();
|
__mmi_lock();
|
||||||
for (m = _weaken(_mmi), i = 0; i < m->i; ++i) {
|
for (m = &_mmi, i = 0; i < m->i; ++i) {
|
||||||
x = m->p[i].x;
|
x = m->p[i].x;
|
||||||
y = m->p[i].y;
|
y = m->p[i].y;
|
||||||
p = __asan_format_interval(p, x << 16, (y << 16) + (FRAMESIZE - 1));
|
p = __asan_format_interval(p, x << 16, (y << 16) + (FRAMESIZE - 1));
|
||||||
|
@ -1396,7 +1394,7 @@ void __asan_map_shadow(uintptr_t p, size_t n) {
|
||||||
kprintf("error: %p size %'zu overlaps shadow space\n", p, n);
|
kprintf("error: %p size %'zu overlaps shadow space\n", p, n);
|
||||||
_Exit(1);
|
_Exit(1);
|
||||||
}
|
}
|
||||||
m = _weaken(_mmi);
|
m = &_mmi;
|
||||||
a = (0x7fff8000 + (p >> 3)) >> 16;
|
a = (0x7fff8000 + (p >> 3)) >> 16;
|
||||||
b = (0x7fff8000 + (p >> 3) + (n >> 3) + 0xffff) >> 16;
|
b = (0x7fff8000 + (p >> 3) + (n >> 3) + 0xffff) >> 16;
|
||||||
for (; a <= b; a += i) {
|
for (; a <= b; a += i) {
|
||||||
|
@ -1413,12 +1411,11 @@ void __asan_map_shadow(uintptr_t p, size_t n) {
|
||||||
addr = (void *)ADDR_32_TO_48(a);
|
addr = (void *)ADDR_32_TO_48(a);
|
||||||
prot = PROT_READ | PROT_WRITE;
|
prot = PROT_READ | PROT_WRITE;
|
||||||
flag = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
|
flag = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
|
||||||
sm = _weaken(sys_mmap)(addr, size, prot, flag, -1, 0);
|
sm = sys_mmap(addr, size, prot, flag, -1, 0);
|
||||||
if (sm.addr == MAP_FAILED ||
|
if (sm.addr == MAP_FAILED ||
|
||||||
_weaken(__track_memory)(m, a, a + i - 1, sm.maphandle,
|
__track_memory(m, a, a + i - 1, sm.maphandle, PROT_READ | PROT_WRITE,
|
||||||
PROT_READ | PROT_WRITE,
|
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, false, false, 0,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, false,
|
size) == -1) {
|
||||||
false, 0, size) == -1) {
|
|
||||||
kprintf("error: could not map asan shadow memory\n");
|
kprintf("error: could not map asan shadow memory\n");
|
||||||
__asan_die()();
|
__asan_die()();
|
||||||
__asan_unreachable();
|
__asan_unreachable();
|
||||||
|
@ -1539,9 +1536,6 @@ void __asan_init(int argc, char **argv, char **envp, unsigned long *auxv) {
|
||||||
__write_str("error: asan binaries require windows10\r\n");
|
__write_str("error: asan binaries require windows10\r\n");
|
||||||
_Exit(0); /* So `make MODE=dbg test` passes w/ Windows7 */
|
_Exit(0); /* So `make MODE=dbg test` passes w/ Windows7 */
|
||||||
}
|
}
|
||||||
REQUIRE(_mmi);
|
|
||||||
REQUIRE(sys_mmap);
|
|
||||||
REQUIRE(__track_memory);
|
|
||||||
if (_weaken(hook_malloc) || _weaken(hook_calloc) || _weaken(hook_realloc) ||
|
if (_weaken(hook_malloc) || _weaken(hook_calloc) || _weaken(hook_realloc) ||
|
||||||
_weaken(hook_realloc_in_place) || _weaken(hook_free) ||
|
_weaken(hook_realloc_in_place) || _weaken(hook_free) ||
|
||||||
_weaken(hook_malloc_usable_size)) {
|
_weaken(hook_malloc_usable_size)) {
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
|
|
||||||
// TODO(jart): Fork this function into ASAN and non-ASAN versions.
|
|
||||||
const char *DescribeFlags(char *p, size_t n, const struct DescribeFlags *d,
|
const char *DescribeFlags(char *p, size_t n, const struct DescribeFlags *d,
|
||||||
size_t m, const char *prefix, unsigned x) {
|
size_t m, const char *prefix, unsigned x) {
|
||||||
bool t;
|
bool t;
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
|
|
||||||
dontasan unsigned __find_memory(const struct MemoryIntervals *mm, int x) {
|
dontasan unsigned __find_memory(const struct MemoryIntervals *mm, int x) {
|
||||||
|
@ -31,6 +30,5 @@ dontasan unsigned __find_memory(const struct MemoryIntervals *mm, int x) {
|
||||||
r = m;
|
r = m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unassert(l == mm->i || x <= mm->p[l].y);
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
|
@ -487,6 +487,10 @@ static errno_t CloneSilicon(int (*fn)(void *, int), char *stk, size_t stksz,
|
||||||
if (!(res = __syslib->pthread_create(&th, 0, SiliconThreadMain, wt)) &&
|
if (!(res = __syslib->pthread_create(&th, 0, SiliconThreadMain, wt)) &&
|
||||||
(flags & CLONE_PARENT_SETTID)) {
|
(flags & CLONE_PARENT_SETTID)) {
|
||||||
*ptid = tid;
|
*ptid = tid;
|
||||||
|
if (flags & CLONE_SETTLS) {
|
||||||
|
struct CosmoTib *tib = tls;
|
||||||
|
tib[-1].tib_pthread = th;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ COSMOPOLITAN_C_START_
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
||||||
#define SYSLIB_VERSION 1
|
#define SYSLIB_VERSION 2
|
||||||
|
|
||||||
typedef uint64_t dispatch_time_t;
|
typedef uint64_t dispatch_time_t;
|
||||||
typedef uint64_t dispatch_semaphore_t;
|
typedef uint64_t dispatch_semaphore_t;
|
||||||
|
@ -42,6 +42,8 @@ struct Syslib {
|
||||||
long (*dispatch_semaphore_signal)(dispatch_semaphore_t);
|
long (*dispatch_semaphore_signal)(dispatch_semaphore_t);
|
||||||
long (*dispatch_semaphore_wait)(dispatch_semaphore_t, dispatch_time_t);
|
long (*dispatch_semaphore_wait)(dispatch_semaphore_t, dispatch_time_t);
|
||||||
dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t);
|
dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t);
|
||||||
|
/* v2 (2023-09-10) */
|
||||||
|
long (*pthread_self)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct Syslib *__syslib;
|
extern struct Syslib *__syslib;
|
||||||
|
|
|
@ -89,6 +89,8 @@ dos kNtErrorUnexpNetErr ECONNABORTED
|
||||||
dos kNtErrorWorkingSetQuota ENOMEM
|
dos kNtErrorWorkingSetQuota ENOMEM
|
||||||
dos kNtErrorWriteProtect EACCES
|
dos kNtErrorWriteProtect EACCES
|
||||||
dos kNtErrorWrongDisk EACCES
|
dos kNtErrorWrongDisk EACCES
|
||||||
|
dos kNtErrorExeMarkedInvalid ENOEXEC
|
||||||
|
dos kNtErrorExeMachineTypeMismatch ENOEXEC
|
||||||
dos WSAEACCES EACCES
|
dos WSAEACCES EACCES
|
||||||
dos WSAEDISCON EPIPE
|
dos WSAEDISCON EPIPE
|
||||||
dos WSAEFAULT EFAULT
|
dos WSAEFAULT EFAULT
|
||||||
|
|
15
libc/sysv/dos2errno/ENOEXEC.S
Normal file
15
libc/sysv/dos2errno/ENOEXEC.S
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// generated by libc/sysv/dos2errno.sh
|
||||||
|
#include "libc/nt/errors.h"
|
||||||
|
#ifndef __x86_64__
|
||||||
|
.end
|
||||||
|
#endif
|
||||||
|
.macro .e doscode systemv
|
||||||
|
.short \doscode
|
||||||
|
.long \systemv
|
||||||
|
.endm
|
||||||
|
.section .sort.rodata.dos2errno.2,"a",@progbits
|
||||||
|
.globl kDos2Errno.ENOEXEC
|
||||||
|
.type kDos2Errno.ENOEXEC,@object
|
||||||
|
kDos2Errno.ENOEXEC:
|
||||||
|
.e kNtErrorExeMarkedInvalid,ENOEXEC
|
||||||
|
.e kNtErrorExeMachineTypeMismatch,ENOEXEC
|
|
@ -17,6 +17,8 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
|
@ -31,9 +33,24 @@ __static_yoink("zipos");
|
||||||
*/
|
*/
|
||||||
void testlib_extract(const char *zip, const char *to, int mode) {
|
void testlib_extract(const char *zip, const char *to, int mode) {
|
||||||
int fdin, fdout;
|
int fdin, fdout;
|
||||||
ASSERT_NE(-1, (fdin = open(zip, O_RDONLY)));
|
if ((fdin = open(zip, O_RDONLY)) == -1) {
|
||||||
ASSERT_NE(-1, (fdout = creat(to, mode)));
|
perror(zip);
|
||||||
ASSERT_NE(-1, copyfd(fdin, fdout, -1));
|
exit(1);
|
||||||
ASSERT_NE(-1, close(fdout));
|
}
|
||||||
ASSERT_NE(-1, close(fdin));
|
if ((fdout = creat(to, mode)) == -1) {
|
||||||
|
perror(to);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (copyfd(fdin, fdout, -1) == -1) {
|
||||||
|
perror(zip);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (close(fdout)) {
|
||||||
|
perror(to);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (close(fdin)) {
|
||||||
|
perror(zip);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ struct PosixThread {
|
||||||
char *tls; // bottom of tls allocation
|
char *tls; // bottom of tls allocation
|
||||||
struct CosmoTib *tib; // middle of tls allocation
|
struct CosmoTib *tib; // middle of tls allocation
|
||||||
struct Dll list; // list of threads
|
struct Dll list; // list of threads
|
||||||
|
pthread_t next; // for xnu silicon
|
||||||
jmp_buf exiter; // for pthread_exit
|
jmp_buf exiter; // for pthread_exit
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
struct _pthread_cleanup_buffer *cleanup;
|
struct _pthread_cleanup_buffer *cleanup;
|
||||||
|
|
|
@ -78,6 +78,7 @@ static int PosixThread(void *arg, int tid) {
|
||||||
}
|
}
|
||||||
// set long jump handler so pthread_exit can bring control back here
|
// set long jump handler so pthread_exit can bring control back here
|
||||||
if (!setjmp(pt->exiter)) {
|
if (!setjmp(pt->exiter)) {
|
||||||
|
pt->next = __get_tls()->tib_pthread;
|
||||||
__get_tls()->tib_pthread = (pthread_t)pt;
|
__get_tls()->tib_pthread = (pthread_t)pt;
|
||||||
unassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0));
|
unassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0));
|
||||||
rc = pt->start(pt->arg);
|
rc = pt->start(pt->arg);
|
||||||
|
|
|
@ -31,8 +31,6 @@
|
||||||
#include "libc/testlib/subprocess.h"
|
#include "libc/testlib/subprocess.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
#ifdef __x86_64__
|
|
||||||
|
|
||||||
#define N 16
|
#define N 16
|
||||||
|
|
||||||
char *GenBuf(char buf[8], int x) {
|
char *GenBuf(char buf[8], int x) {
|
||||||
|
@ -69,6 +67,7 @@ TEST(execve, testArgPassing) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(execve, ziposELF) {
|
TEST(execve, ziposELF) {
|
||||||
|
if (1) return; // TODO: rewrite
|
||||||
if (IsFreebsd()) return; // TODO: fixme on freebsd
|
if (IsFreebsd()) return; // TODO: fixme on freebsd
|
||||||
if (IsLinux() && !__is_linux_2_6_23()) return; // TODO: fixme on old linux
|
if (IsLinux() && !__is_linux_2_6_23()) return; // TODO: fixme on old linux
|
||||||
if (!IsLinux() && !IsFreebsd()) {
|
if (!IsLinux() && !IsFreebsd()) {
|
||||||
|
@ -83,6 +82,7 @@ TEST(execve, ziposELF) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(execve, ziposAPE) {
|
TEST(execve, ziposAPE) {
|
||||||
|
if (1) return; // TODO: rewrite
|
||||||
if (IsFreebsd()) return; // TODO: fixme on freebsd
|
if (IsFreebsd()) return; // TODO: fixme on freebsd
|
||||||
if (IsLinux() && !__is_linux_2_6_23()) return; // TODO: fixme on old linux
|
if (IsLinux() && !__is_linux_2_6_23()) return; // TODO: fixme on old linux
|
||||||
if (!IsLinux() && !IsFreebsd()) {
|
if (!IsLinux() && !IsFreebsd()) {
|
||||||
|
@ -150,5 +150,3 @@ BENCH(execve, bench) {
|
||||||
EZBENCH2("execve", donothing, ExecveTinyElf(path));
|
EZBENCH2("execve", donothing, ExecveTinyElf(path));
|
||||||
unlink(path);
|
unlink(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -72,7 +72,6 @@ TEST(siocgifconf, test) {
|
||||||
ASSERT_NE(-1, close(socketfd));
|
ASSERT_NE(-1, close(socketfd));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __x86_64__
|
|
||||||
TEST(siocgifconf, mkntenvblock_systemroot) {
|
TEST(siocgifconf, mkntenvblock_systemroot) {
|
||||||
if (__argc != 1) return;
|
if (__argc != 1) return;
|
||||||
SPAWN(fork);
|
SPAWN(fork);
|
||||||
|
@ -81,7 +80,6 @@ TEST(siocgifconf, mkntenvblock_systemroot) {
|
||||||
abort();
|
abort();
|
||||||
EXITS(0);
|
EXITS(0);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST(fionread, pipe) {
|
TEST(fionread, pipe) {
|
||||||
int pfds[2];
|
int pfds[2];
|
||||||
|
|
|
@ -89,7 +89,6 @@ __attribute__((__constructor__)) static void init(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __x86_64__
|
|
||||||
TEST(sched_setaffinity, isInheritedAcrossExecve) {
|
TEST(sched_setaffinity, isInheritedAcrossExecve) {
|
||||||
cpu_set_t x;
|
cpu_set_t x;
|
||||||
CPU_ZERO(&x);
|
CPU_ZERO(&x);
|
||||||
|
@ -104,7 +103,6 @@ TEST(sched_setaffinity, isInheritedAcrossExecve) {
|
||||||
EXPECT_TRUE(WIFEXITED(ws));
|
EXPECT_TRUE(WIFEXITED(ws));
|
||||||
EXPECT_EQ(42, WEXITSTATUS(ws));
|
EXPECT_EQ(42, WEXITSTATUS(ws));
|
||||||
}
|
}
|
||||||
#endif /* __x86_64__ */
|
|
||||||
|
|
||||||
TEST(sched_getaffinity, getpid) {
|
TEST(sched_getaffinity, getpid) {
|
||||||
cpu_set_t x, y;
|
cpu_set_t x, y;
|
||||||
|
|
|
@ -90,7 +90,7 @@ o/$(MODE)/test/libc/mem/prog/life.elf: \
|
||||||
o/$(MODE)/test/libc/mem/prog/life.elf
|
o/$(MODE)/test/libc/mem/prog/life.elf
|
||||||
@$(COMPILE) -wAASSIMILATE -T$@ \
|
@$(COMPILE) -wAASSIMILATE -T$@ \
|
||||||
$(VM) \
|
$(VM) \
|
||||||
o/$(MODE)/tool/build/assimilate.com -cf \
|
o/$(MODE)/tool/build/assimilate.com -bcef \
|
||||||
o/$(MODE)/test/libc/mem/prog/life.elf
|
o/$(MODE)/test/libc/mem/prog/life.elf
|
||||||
|
|
||||||
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o: private \
|
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o: private \
|
||||||
|
@ -99,6 +99,12 @@ o/$(MODE)/test/libc/mem/prog/life.elf.zip.o: private \
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
o/$(MODE)/test/libc/mem/prog/life.com.zip.o: private \
|
||||||
|
ZIPOBJ_FLAGS += \
|
||||||
|
-B
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
o/$(MODE)/test/libc/mem/prog/sock.com.dbg: \
|
o/$(MODE)/test/libc/mem/prog/sock.com.dbg: \
|
||||||
$(LIBC_RUNTIME) \
|
$(LIBC_RUNTIME) \
|
||||||
$(LIBC_SOCK) \
|
$(LIBC_SOCK) \
|
||||||
|
@ -117,7 +123,7 @@ o/$(MODE)/test/libc/mem/prog/sock.elf: \
|
||||||
o/$(MODE)/test/libc/mem/prog/sock.elf
|
o/$(MODE)/test/libc/mem/prog/sock.elf
|
||||||
@$(COMPILE) -wAASSIMILATE -T$@ \
|
@$(COMPILE) -wAASSIMILATE -T$@ \
|
||||||
$(VM) \
|
$(VM) \
|
||||||
o/$(MODE)/tool/build/assimilate.com -cf \
|
o/$(MODE)/tool/build/assimilate.com -cef \
|
||||||
o/$(MODE)/test/libc/mem/prog/sock.elf
|
o/$(MODE)/test/libc/mem/prog/sock.elf
|
||||||
|
|
||||||
o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o: private \
|
o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o: private \
|
||||||
|
|
|
@ -17,14 +17,20 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/stdio/posix_spawn.h"
|
#include "libc/stdio/posix_spawn.h"
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/atomic.h"
|
#include "libc/atomic.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
|
#include "libc/calls/struct/rusage.h"
|
||||||
#include "libc/calls/struct/sigaction.h"
|
#include "libc/calls/struct/sigaction.h"
|
||||||
|
#include "libc/calls/struct/stat.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/safemacros.internal.h"
|
#include "libc/intrin/safemacros.internal.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/mem/gc.h"
|
#include "libc/mem/gc.h"
|
||||||
|
#include "libc/mem/gc.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
|
@ -33,15 +39,52 @@
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/auxv.h"
|
#include "libc/sysv/consts/auxv.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/sysv/consts/rusage.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/testlib/ezbench.h"
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/nsync/mu.h"
|
#include "third_party/nsync/mu.h"
|
||||||
#ifdef __x86_64__
|
|
||||||
|
const char kTinyLinuxExit[128] = {
|
||||||
|
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, // ⌂ELF☻☺☺
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
|
||||||
|
0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, // ☻ > ☺
|
||||||
|
0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // x @
|
||||||
|
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // @
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, // @ 8
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ☺
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // ☺ ♣
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
|
||||||
|
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // @
|
||||||
|
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // @
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ç
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ç
|
||||||
|
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ►
|
||||||
|
0x6a, 0x2a, 0x5f, 0x6a, 0x3c, 0x58, 0x0f, 0x05, // j*_j<X☼♣
|
||||||
|
};
|
||||||
|
|
||||||
char testlib_enable_tmp_setup_teardown;
|
char testlib_enable_tmp_setup_teardown;
|
||||||
|
|
||||||
|
char *GetHost(void) {
|
||||||
|
static char host[256];
|
||||||
|
unassert(!gethostname(host, sizeof(host)));
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
long GetRss(void) {
|
||||||
|
struct rusage ru;
|
||||||
|
unassert(!getrusage(RUSAGE_SELF, &ru));
|
||||||
|
return ru.ru_maxrss * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
long GetSize(const char *path) {
|
||||||
|
struct stat st;
|
||||||
|
unassert(!stat(path, &st));
|
||||||
|
return st.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((__constructor__)) static void init(void) {
|
__attribute__((__constructor__)) static void init(void) {
|
||||||
switch (atoi(nulltoempty(getenv("THE_DOGE")))) {
|
switch (atoi(nulltoempty(getenv("THE_DOGE")))) {
|
||||||
case 42:
|
case 42:
|
||||||
|
@ -51,7 +94,7 @@ __attribute__((__constructor__)) static void init(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(posix_spawn, test) {
|
TEST(posix_spawn, self) {
|
||||||
int ws, pid;
|
int ws, pid;
|
||||||
char *prog = GetProgramExecutableName();
|
char *prog = GetProgramExecutableName();
|
||||||
char *args[] = {prog, NULL};
|
char *args[] = {prog, NULL};
|
||||||
|
@ -62,6 +105,31 @@ TEST(posix_spawn, test) {
|
||||||
EXPECT_EQ(42, WEXITSTATUS(ws));
|
EXPECT_EQ(42, WEXITSTATUS(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(posix_spawn, ape) {
|
||||||
|
int ws, pid;
|
||||||
|
char *prog = "./life.com";
|
||||||
|
char *args[] = {prog, 0};
|
||||||
|
char *envs[] = {0};
|
||||||
|
testlib_extract("/zip/life.com", prog, 0755);
|
||||||
|
ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
|
||||||
|
ASSERT_NE(-1, waitpid(pid, &ws, 0));
|
||||||
|
ASSERT_TRUE(WIFEXITED(ws));
|
||||||
|
ASSERT_EQ(42, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(posix_spawn, elf) {
|
||||||
|
if (IsXnu() || IsWindows() || IsMetal()) return;
|
||||||
|
int ws, pid;
|
||||||
|
char *prog = "./life.elf"; // assimilate -bcef
|
||||||
|
char *args[] = {prog, 0};
|
||||||
|
char *envs[] = {0};
|
||||||
|
testlib_extract("/zip/life.elf", prog, 0755);
|
||||||
|
ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
|
||||||
|
ASSERT_NE(-1, waitpid(pid, &ws, 0));
|
||||||
|
ASSERT_TRUE(WIFEXITED(ws));
|
||||||
|
ASSERT_EQ(42, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(posix_spawn, pipe) {
|
TEST(posix_spawn, pipe) {
|
||||||
char buf[10];
|
char buf[10];
|
||||||
int p[2], pid, status;
|
int p[2], pid, status;
|
||||||
|
@ -151,51 +219,62 @@ TEST(posix_spawn, agony) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
* BEST LINUX FORK+EXEC+EXIT+WAIT PERFORMANCE
|
|
||||||
* The fastest it can go with fork() is 40µs
|
|
||||||
* The fastest it can go with vfork() is 26µs
|
|
||||||
*/
|
|
||||||
|
|
||||||
void BenchmarkProcessLifecycle(void) {
|
void ForkExecveWait(const char *prog) {
|
||||||
|
int ws;
|
||||||
|
if (!fork()) {
|
||||||
|
execve(prog, (char *[]){(char *)prog, 0}, (char *[]){0});
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
ASSERT_NE(-1, wait(&ws));
|
||||||
|
ASSERT_EQ(42, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VforkExecveWait(const char *prog) {
|
||||||
|
int ws;
|
||||||
|
if (!vfork()) {
|
||||||
|
execve(prog, (char *[]){(char *)prog, 0}, (char *[]){0});
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
ASSERT_NE(-1, wait(&ws));
|
||||||
|
ASSERT_EQ(42, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PosixSpawnWait(const char *prog) {
|
||||||
int ws, pid;
|
int ws, pid;
|
||||||
char *prog = "tiny64";
|
char *args[] = {(char *)prog, 0};
|
||||||
char *args[] = {"tiny64", NULL};
|
char *envs[] = {0};
|
||||||
char *envs[] = {NULL};
|
|
||||||
ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
|
ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
|
||||||
ASSERT_NE(-1, waitpid(pid, &ws, 0));
|
ASSERT_NE(-1, waitpid(pid, &ws, 0));
|
||||||
ASSERT_TRUE(WIFEXITED(ws));
|
ASSERT_TRUE(WIFEXITED(ws));
|
||||||
ASSERT_EQ(42, WEXITSTATUS(ws));
|
ASSERT_EQ(42, WEXITSTATUS(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char kTinyLinuxExit[128] = {
|
TEST(posix_spawn, bench) {
|
||||||
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, // ⌂ELF☻☺☺
|
long n = 128L * 1000 * 1000;
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
|
memset(gc(malloc(n)), -1, n);
|
||||||
0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, // ☻ > ☺
|
creat("tiny64", 0755);
|
||||||
0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // x @
|
write(3, kTinyLinuxExit, 128);
|
||||||
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // @
|
close(3);
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
|
testlib_extract("/zip/life.com", "life.com", 0755);
|
||||||
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, // @ 8
|
testlib_extract("/zip/life.elf", "life.elf", 0755);
|
||||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ☺
|
kprintf("%s %s (MODE=" MODE
|
||||||
0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // ☺ ♣
|
" rss=%'zu tiny64=%'zu life.com=%'zu life.elf=%'zu)\n",
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
|
__describe_os(), GetHost(), GetRss(), GetSize("tiny64"),
|
||||||
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // @
|
GetSize("life.com"), GetSize("life.elf"));
|
||||||
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // @
|
ForkExecveWait("./life.com");
|
||||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ç
|
EZBENCH2("posix_spawn life.com", donothing, PosixSpawnWait("./life.com"));
|
||||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ç
|
EZBENCH2("vfork life.com", donothing, VforkExecveWait("./life.com"));
|
||||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ►
|
EZBENCH2("fork life.com", donothing, ForkExecveWait("./life.com"));
|
||||||
0x6a, 0x2a, 0x5f, 0x6a, 0x3c, 0x58, 0x0f, 0x05, // j*_j<X☼♣
|
if (IsXnu() || IsWindows() || IsMetal()) return;
|
||||||
};
|
EZBENCH2("posix_spawn life.elf", donothing, PosixSpawnWait("./life.elf"));
|
||||||
|
EZBENCH2("vfork life.elf", donothing, VforkExecveWait("./life.elf"));
|
||||||
/* BENCH(spawn, bench) { */
|
EZBENCH2("fork life.elf", donothing, ForkExecveWait("./life.elf"));
|
||||||
/* int fd; */
|
#ifdef __x86_64__
|
||||||
/* if (IsLinux()) { */
|
if (!IsLinux()) return;
|
||||||
/* fd = open("tiny64", O_CREAT | O_TRUNC | O_WRONLY, 0755); */
|
EZBENCH2("posix_spawn tiny64", donothing, PosixSpawnWait("tiny64"));
|
||||||
/* write(fd, kTinyLinuxExit, 128); */
|
EZBENCH2("vfork tiny64", donothing, VforkExecveWait("tiny64"));
|
||||||
/* close(fd); */
|
EZBENCH2("fork tiny64", donothing, ForkExecveWait("tiny64"));
|
||||||
/* EZBENCH2("spawn", donothing, BenchmarkProcessLifecycle()); */
|
#endif
|
||||||
/* unlink("tiny64"); */
|
}
|
||||||
/* } */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
|
||||||
|
|
|
@ -85,11 +85,16 @@ o/$(MODE)/test/libc/stdio/popen_test.com.dbg: \
|
||||||
$(APE_NO_MODIFY_SELF)
|
$(APE_NO_MODIFY_SELF)
|
||||||
@$(APELINK)
|
@$(APELINK)
|
||||||
|
|
||||||
|
o/$(MODE)/test/libc/stdio/posix_spawn_test.com.runs: \
|
||||||
|
private QUOTA += -M8192m
|
||||||
|
|
||||||
o/$(MODE)/test/libc/stdio/posix_spawn_test.com.dbg: \
|
o/$(MODE)/test/libc/stdio/posix_spawn_test.com.dbg: \
|
||||||
$(TEST_LIBC_STDIO_DEPS) \
|
$(TEST_LIBC_STDIO_DEPS) \
|
||||||
o/$(MODE)/test/libc/stdio/posix_spawn_test.o \
|
o/$(MODE)/test/libc/stdio/posix_spawn_test.o \
|
||||||
o/$(MODE)/test/libc/stdio/stdio.pkg \
|
o/$(MODE)/test/libc/stdio/stdio.pkg \
|
||||||
o/$(MODE)/tool/build/echo.com.zip.o \
|
o/$(MODE)/tool/build/echo.com.zip.o \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/life.com.zip.o \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \
|
||||||
$(LIBC_TESTMAIN) \
|
$(LIBC_TESTMAIN) \
|
||||||
$(CRT) \
|
$(CRT) \
|
||||||
$(APE_NO_MODIFY_SELF)
|
$(APE_NO_MODIFY_SELF)
|
||||||
|
|
|
@ -396,7 +396,7 @@ void Recv(struct Client *client, void *output, size_t outputsize) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendProgramOutut(struct Client *client) {
|
void SendProgramOutput(struct Client *client) {
|
||||||
if (client->output) {
|
if (client->output) {
|
||||||
SendOutputFragmentMessage(kRunitStderr, client->output,
|
SendOutputFragmentMessage(kRunitStderr, client->output,
|
||||||
appendz(client->output).i);
|
appendz(client->output).i);
|
||||||
|
@ -570,7 +570,7 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
WARNF("killing %d %s and hanging up %d due to interrupt", client->fd,
|
WARNF("killing %d %s and hanging up %d due to interrupt", client->fd,
|
||||||
exename, client->pid);
|
exename, client->pid);
|
||||||
HangupClientAndTerminateJob:
|
HangupClientAndTerminateJob:
|
||||||
SendProgramOutut(client);
|
SendProgramOutput(client);
|
||||||
mbedtls_ssl_close_notify(&ezssl);
|
mbedtls_ssl_close_notify(&ezssl);
|
||||||
TerminateJob:
|
TerminateJob:
|
||||||
PrintProgramOutput(client);
|
PrintProgramOutput(client);
|
||||||
|
@ -687,7 +687,7 @@ WaitAgain:
|
||||||
AppendResourceReport(&client->output, &rusage, "\n");
|
AppendResourceReport(&client->output, &rusage, "\n");
|
||||||
PrintProgramOutput(client);
|
PrintProgramOutput(client);
|
||||||
}
|
}
|
||||||
SendProgramOutut(client);
|
SendProgramOutput(client);
|
||||||
SendExitMessage(exitcode);
|
SendExitMessage(exitcode);
|
||||||
mbedtls_ssl_close_notify(&ezssl);
|
mbedtls_ssl_close_notify(&ezssl);
|
||||||
if (etxtbsy_tries) {
|
if (etxtbsy_tries) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue