mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-07 02:10:27 +00:00
Merge branch 'master' into import-modc
This commit is contained in:
commit
86ddfa2cee
1046 changed files with 13863 additions and 7165 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,4 +11,3 @@ __pycache__
|
|||
/TAGS
|
||||
/bx_enh_dbg.ini
|
||||
/tool/emacs/*.elc
|
||||
/usr/share/dict/words
|
||||
|
|
23
Makefile
23
Makefile
|
@ -59,9 +59,9 @@
|
|||
#
|
||||
# build/config.mk
|
||||
|
||||
SHELL = /bin/sh
|
||||
SHELL = build/bootstrap/cocmd.com
|
||||
HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 win7 win10 xnu
|
||||
SANITY := $(shell build/sanitycheck $$PPID)
|
||||
#SANITY := $(shell build/sanitycheck $$PPID)
|
||||
|
||||
.SUFFIXES:
|
||||
.DELETE_ON_ERROR:
|
||||
|
@ -229,7 +229,7 @@ depend: o/$(MODE)/depend
|
|||
tags: TAGS HTAGS
|
||||
|
||||
o/$(MODE)/.x:
|
||||
@mkdir -p $(@D) && touch $@
|
||||
@$(COMPILE) -AMKDIR -tT$@ $(MKDIR) $(@D)
|
||||
|
||||
o/$(MODE)/srcs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x))))
|
||||
$(file >$@,$(SRCS))
|
||||
|
@ -246,11 +246,11 @@ o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS
|
|||
$(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x)))
|
||||
|
||||
TAGS: o/$(MODE)/srcs-old.txt $(SRCS)
|
||||
@rm -f $@
|
||||
@$(RM) $@
|
||||
@$(COMPILE) -ATAGS -T$@ $(TAGS) $(TAGSFLAGS) -L $< -o $@
|
||||
|
||||
HTAGS: o/$(MODE)/hdrs-old.txt $(HDRS)
|
||||
@rm -f $@
|
||||
@$(RM) $@
|
||||
@$(COMPILE) -ATAGS -T$@ build/htags -L $< -o $@
|
||||
|
||||
loc: o/$(MODE)/tool/build/summy.com
|
||||
|
@ -258,6 +258,7 @@ loc: o/$(MODE)/tool/build/summy.com
|
|||
$(XARGS) wc -l | grep total | awk '{print $$1}' | $<
|
||||
|
||||
COSMOPOLITAN_OBJECTS = \
|
||||
NET_HTTP \
|
||||
LIBC_DNS \
|
||||
LIBC_SOCK \
|
||||
LIBC_NT_WS2_32 \
|
||||
|
@ -331,6 +332,7 @@ COSMOPOLITAN_HEADERS = \
|
|||
LIBC_UNICODE \
|
||||
LIBC_X \
|
||||
LIBC_ZIPOS \
|
||||
NET_HTTP \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
|
@ -352,9 +354,10 @@ o/cosmopolitan.h: \
|
|||
o/cosmopolitan.html: \
|
||||
o/$(MODE)/third_party/chibicc/chibicc.com.dbg \
|
||||
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))
|
||||
$(file >$@.args,$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))))
|
||||
o/$(MODE)/third_party/chibicc/chibicc.com.dbg -J \
|
||||
-fno-common -include libc/integral/normalize.inc -o $@ \
|
||||
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))
|
||||
@$@.args
|
||||
|
||||
$(SRCS): \
|
||||
libc/integral/normalize.inc \
|
||||
|
@ -388,9 +391,9 @@ $(SRCS):
|
|||
$(HDRS):
|
||||
$(INCS):
|
||||
.DEFAULT:
|
||||
@echo >&2
|
||||
@echo NOTE: deleting o/$(MODE)/depend because of an unspecified prerequisite: $@ >&2
|
||||
@echo >&2
|
||||
rm -f o/$(MODE)/depend
|
||||
@$(ECHO) >&2
|
||||
@$(ECHO) NOTE: deleting o/$(MODE)/depend because of an unspecified prerequisite: $@ >&2
|
||||
@$(ECHO) >&2
|
||||
$(RM) o/$(MODE)/depend
|
||||
|
||||
-include o/$(MODE)/depend
|
||||
|
|
128
README.md
128
README.md
|
@ -28,21 +28,61 @@ printf 'main() { printf("hello world\\n"); }\n' >hello.c
|
|||
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
|
||||
-fno-omit-frame-pointer -pg -mnop-mcount \
|
||||
-o hello.com.dbg hello.c -fuse-ld=bfd -Wl,-T,ape.lds \
|
||||
-include cosmopolitan.h crt.o ape.o cosmopolitan.a
|
||||
-include cosmopolitan.h crt.o ape-no-modify-self.o cosmopolitan.a
|
||||
objcopy -S -O binary hello.com.dbg hello.com
|
||||
```
|
||||
|
||||
You now have a portable program. Please note that your APE binary will
|
||||
assimilate itself as a conventional resident of your platform after the
|
||||
first run, so it can be fast and efficient for subsequent executions.
|
||||
You now have a portable program.
|
||||
|
||||
```sh
|
||||
./hello.com
|
||||
bash -c './hello.com' # zsh/fish workaround (we upstreamed patches)
|
||||
bash -c './hello.com' # zsh/fish workaround (we patched them in 2021)
|
||||
```
|
||||
|
||||
So if you intend to copy the binary to Windows or Mac then please do
|
||||
that before you run it, not after.
|
||||
Since we used the `ape-no-modify-self.o` bootloader (rather than
|
||||
`ape.o`) your executable will not modify itself when it's run. What
|
||||
it'll instead do, is extract a 4kb program to `${TMPDIR:-/tmp}` that
|
||||
maps your program into memory without needing to copy it. It's possible
|
||||
to install the APE loader systemwide as follows.
|
||||
|
||||
```sh
|
||||
# (1) linux systems that want binfmt_misc
|
||||
ape/apeinstall.sh
|
||||
|
||||
# (2) for linux/freebsd/netbsd/openbsd systems
|
||||
cp build/bootstrap/ape.elf /usr/bin/ape
|
||||
|
||||
# (3) for mac os x systems
|
||||
cp build/bootstrap/ape.macho /usr/bin/ape
|
||||
```
|
||||
|
||||
If you followed steps (2) and (3) then there's going to be a slight
|
||||
constant-time startup latency each time you run an APE binary. Your
|
||||
system might also prevent your APE program from being installed to a
|
||||
system directory as a setuid binary or a script interpreter. To solve
|
||||
that, you can use the following flag to turn your binary into the
|
||||
platform local format (ELF or Mach-O):
|
||||
|
||||
```sh
|
||||
$ file hello.com
|
||||
hello.com: DOS/MBR boot sector
|
||||
./hello.com --assimilate
|
||||
$ file hello.com
|
||||
hello.com: ELF 64-bit LSB executable
|
||||
```
|
||||
|
||||
There's also some other useful flags that get baked into your binary by
|
||||
default:
|
||||
|
||||
```sh
|
||||
./hello.com --strace # log system calls to stderr
|
||||
./hello.com --ftrace # log function calls to stderr
|
||||
```
|
||||
|
||||
If you want your `hello.com` program to be much tinier, more on the
|
||||
order of 16kb rather than 60kb, then all you have to do is use
|
||||
<https://justine.lol/cosmopolitan/cosmopolitan-tiny.zip> instead. See
|
||||
<https://justine.lol/cosmopolitan/download.html>.
|
||||
|
||||
### MacOS
|
||||
|
||||
|
@ -64,22 +104,85 @@ Windows](https://justine.lol/cosmopolitan/windows-compiling.html)
|
|||
tutorial. It's needed because the ELF object format is what makes
|
||||
universal binaries possible.
|
||||
|
||||
Cosmopolitan officially only builds on Linux. However, one highly
|
||||
experimental (and currently broken) thing you could try, is building the
|
||||
entire cosmo repository from source using the cross9 toolchain.
|
||||
|
||||
```
|
||||
mkdir -p o/third_party
|
||||
rm -rf o/third_party/gcc
|
||||
wget https://justine.lol/linux-compiler-on-windows/cross9.zip
|
||||
unzip cross9.zip
|
||||
mv cross9 o/third_party/gcc
|
||||
build/bootstrap/make.com
|
||||
```
|
||||
|
||||
## Source Builds
|
||||
|
||||
Cosmopolitan can be compiled from source on any Linux distro. GNU make
|
||||
needs to be installed beforehand. This is a freestanding hermetic
|
||||
repository that bootstraps using a vendored static gcc9 executable.
|
||||
No further dependencies are required.
|
||||
Cosmopolitan can be compiled from source on any Linux distro. First, you
|
||||
need to download or clone the repository.
|
||||
|
||||
```sh
|
||||
wget https://justine.lol/cosmopolitan/cosmopolitan.tar.gz
|
||||
tar xf cosmopolitan.tar.gz # see releases page
|
||||
cd cosmopolitan
|
||||
make -j16
|
||||
```
|
||||
|
||||
This will build the entire repository and run all the tests:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make.com -j16
|
||||
o//examples/hello.com
|
||||
find o -name \*.com | xargs ls -rShal | less
|
||||
```
|
||||
|
||||
If you get an error running make.com then it's probably because you have
|
||||
WINE installed to `binfmt_misc`. You can fix that by installing the the
|
||||
APE loader as an interpreter. It'll improve build performance too!
|
||||
|
||||
```sh
|
||||
ape/apeinstall.sh
|
||||
```
|
||||
|
||||
Since the Cosmopolitan repository is very large, you might only want to
|
||||
build a particular thing. Cosmopolitan's build config does a good job at
|
||||
having minimal deterministic builds. For example, if you wanted to build
|
||||
only hello.com then you could do that as follows:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make.com -j16 o//examples/hello.com
|
||||
```
|
||||
|
||||
Sometimes it's desirable to build a subset of targets, without having to
|
||||
list out each individual one. You can do that by asking make to build a
|
||||
directory name. For example, if you wanted to build only the targets and
|
||||
subtargets of the chibicc package including its tests, you would say:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make.com -j16 o//third_party/chibicc
|
||||
o//third_party/chibicc/chibicc.com --help
|
||||
```
|
||||
|
||||
Cosmopolitan provides a variety of build modes. For example, if you want
|
||||
really tiny binaries (as small as 12kb in size) then you'd say:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make.com -j16 MODE=tiny
|
||||
```
|
||||
|
||||
Here's some other build modes you can try:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make.com -j16 MODE=dbg # asan + ubsan + debug
|
||||
build/bootstrap/make.com -j16 MODE=asan # production memory safety
|
||||
build/bootstrap/make.com -j16 MODE=opt # -march=native optimizations
|
||||
build/bootstrap/make.com -j16 MODE=rel # traditional release binaries
|
||||
build/bootstrap/make.com -j16 MODE=optlinux # optimal linux-only performance
|
||||
build/bootstrap/make.com -j16 MODE=tinylinux # tiniest linux-only 4kb binaries
|
||||
```
|
||||
|
||||
For further details, see [//build/config.mk](build/config.mk).
|
||||
|
||||
## GDB
|
||||
|
||||
Here's the recommended `~/.gdbinit` config:
|
||||
|
@ -123,4 +226,3 @@ gdb foo.com -ex 'add-symbol-file foo.com.dbg 0x401000'
|
|||
| FreeBSD | 12 | 2018 |
|
||||
| OpenBSD | 6.4 | 2018 |
|
||||
| NetBSD | 9.1 | 2020 |
|
||||
| GNU Make | 4.0 | 2015 |
|
||||
|
|
221
ape/ape.S
221
ape/ape.S
|
@ -542,27 +542,39 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
|
|||
// present two choices.
|
||||
.ascii "o=\"$(command -v \"$0\")\"\n"
|
||||
// Try to use a system-wide APE loader.
|
||||
.ascii "type ape-loader >/dev/null 2>&1 && "
|
||||
.ascii "exec ape-loader \"$o\" \"$@\"\n"
|
||||
.ascii "[ x\"$1\" != x--assimilate ] && "
|
||||
.ascii "type ape >/dev/null 2>&1 && "
|
||||
.ascii "exec ape \"$o\" \"$@\"\n"
|
||||
#ifdef APE_LOADER
|
||||
// There is no system-wide APE loader, but there is one
|
||||
// embedded inside the APE. So if the system is not MacOs,
|
||||
// extract the loader into a temp folder, and use it to
|
||||
// load the APE without modifying it.
|
||||
.ascii "if [ ! -d /Applications ]; then\n"
|
||||
.ascii "t=\"${TMPDIR:-/tmp}/ape-loader\"\n"
|
||||
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
|
||||
.ascii "t=\"${TMPDIR:-/tmp}/ape\"\n"
|
||||
.ascii "[ -x \"$t\" ] || {\n"
|
||||
.ascii "mkdir -p \"${t%/*}\" &&\n"
|
||||
.ascii "dd if=\"$o\" of=\"$t.$$\" skip=\""
|
||||
.shstub ape_loader_dd_skip,2
|
||||
.ascii "\" count=\""
|
||||
.shstub ape_loader_dd_count,2
|
||||
.ascii "\" bs=64 2>/dev/null &&\n"
|
||||
.ascii "chmod 755 \"$t.$$\" &&\n"
|
||||
.ascii "mv \"$t.$$\" \"$t\"\n"
|
||||
.ascii "\" bs=64 2>/dev/null\n"
|
||||
#if SupportsXnu()
|
||||
.ascii "[ -d /Applications ] && "
|
||||
.ascii "dd if=\"$t.$$\""
|
||||
.ascii " of=\"$t.$$\""
|
||||
.ascii " skip=6"
|
||||
.ascii " count=10"
|
||||
.ascii " bs=64"
|
||||
.ascii " conv=notrunc"
|
||||
.ascii " 2>/dev/null\n"
|
||||
#endif /* SupportsXnu() */
|
||||
.ascii "chmod 755 \"$t.$$\"\n"
|
||||
.ascii "mv -f \"$t.$$\" \"$t\"\n"
|
||||
.ascii "}\n"
|
||||
.ascii "exec \"$t\" \"$o\" \"$@\"\n"
|
||||
.ascii "fi\n"
|
||||
#endif
|
||||
.ascii "}\n"
|
||||
#endif /* APE_LOADER */
|
||||
#ifndef APE_NO_MODIFY_SELF
|
||||
// The default behavior is: to overwrite the header in place.
|
||||
// We prefer this because it's a tiny constant one time cost.
|
||||
|
@ -581,15 +593,42 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
|
|||
// then permission clashes can happen between system users,
|
||||
// since only root is able to set the sticky bit, which can
|
||||
// be addressed simply by overriding the TMPDIR environment
|
||||
.ascii "o=\"${TMPDIR:-/tmp}/$0\"\n"
|
||||
.ascii "if [ ! -e \"$o\" ]; then\n"
|
||||
.ascii "d=\"$o\"\n"
|
||||
.ascii "o=\"$o.$$\"\n"
|
||||
.ascii "mkdir -p \"${o%/*}\" 2>/dev/null\n"
|
||||
.ascii "cp -f \"$0\" \"$o\" || exit 120\n"
|
||||
.ascii "t=\"${TMPDIR:-/tmp}/$0\"\n"
|
||||
.ascii "[ x\"$1\" != x--assimilate ] || [ ! -e \"$t\" ] && {\n"
|
||||
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
|
||||
.ascii "mkdir -p \"${t%/*}\" 2>/dev/null\n"
|
||||
.ascii "cp -f \"$o\" \"$t.$$\" &&\n"
|
||||
.ascii "mv -f \"$t.$$\" \"$t\" || exit 120\n"
|
||||
.ascii "o=\"$t\"\n"
|
||||
.ascii "}\n"
|
||||
#endif /* APE_NO_MODIFY_SELF */
|
||||
.ascii "exec 7<> \"$o\" || exit 121\n"
|
||||
.ascii "printf '"
|
||||
.ascii "\\177ELF" # 0x0: ⌂ELF
|
||||
.ascii "\\2" # 4: long mode
|
||||
.ascii "\\1" # 5: little endian
|
||||
.ascii "\\1" # 6: elf v1.o
|
||||
.ascii "\\011" # 7: FreeBSD
|
||||
.ascii "\\0" # 8: os/abi ver.
|
||||
.ascii "\\0\\0\\0" # 9: padding 3/7
|
||||
.ascii "\\0\\0\\0\\0" # padding 4/7
|
||||
.ascii "\\2\\0" # 10: εxεcµταblε
|
||||
.ascii "\\076\\0" # 12: NexGen32e
|
||||
.ascii "\\1\\0\\0\\0" # 14: elf v1.o
|
||||
.shstub ape_elf_entry,8 # 18: e_entry
|
||||
.shstub ape_elf_phoff,8 # 20: e_phoff
|
||||
.shstub ape_elf_shoff,8 # 28: e_shoff
|
||||
.ascii "\\0\\0\\0\\0" # 30: e_flags
|
||||
.ascii "\\100\\0" # 34: e_ehsize
|
||||
.ascii "\\070\\0" # 36: e_phentsize
|
||||
.shstub ape_elf_phnum,2 # 38: e_phnum
|
||||
.ascii "\\0\\0" # 3a: e_shentsize
|
||||
.shstub ape_elf_shnum,2 # 3c: e_shnum
|
||||
.shstub ape_elf_shstrndx,2 # 3e: e_shstrndx
|
||||
.ascii "' >&7\n"
|
||||
.ascii "exec 7<&-\n"
|
||||
#if SupportsXnu()
|
||||
.ascii "if [ -d /Applications ]; then\n"
|
||||
.ascii "[ -d /Applications ] && "
|
||||
.ascii "dd if=\"$o\""
|
||||
.ascii " of=\"$o\""
|
||||
.ascii " bs=8"
|
||||
|
@ -598,54 +637,21 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
|
|||
.ascii "\" count=\""
|
||||
.shstub ape_macho_dd_count,2
|
||||
.ascii "\" conv=notrunc 2>/dev/null\n"
|
||||
.ascii "el"
|
||||
#endif /* XNU */
|
||||
.ascii "if exec 7<> \"$o\"; then\n"
|
||||
.ascii "printf '"
|
||||
.ascii "\\177ELF" # 0x0: ⌂ELF
|
||||
.ascii "\\2" # 4: long mode
|
||||
.ascii "\\1" # 5: little endian
|
||||
.ascii "\\1" # 6: elf v1.o
|
||||
.ascii "\\011" # 7: FreeBSD
|
||||
.ascii "\\0" # 8: os/abi ver.
|
||||
.ascii "\\0\\0\\0" # 9: padding 3/7
|
||||
.ascii "\\0\\0\\0\\0" # padding 4/7
|
||||
.ascii "\\2\\0" # 10: εxεcµταblε
|
||||
.ascii "\\076\\0" # 12: NexGen32e
|
||||
.ascii "\\1\\0\\0\\0" # 14: elf v1.o
|
||||
.shstub ape_elf_entry,8 # 18: e_entry
|
||||
.shstub ape_elf_phoff,8 # 20: e_phoff
|
||||
.shstub ape_elf_shoff,8 # 28: e_shoff
|
||||
.ascii "\\0\\0\\0\\0" # 30: e_flags
|
||||
.ascii "\\100\\0" # 34: e_ehsize
|
||||
.ascii "\\070\\0" # 36: e_phentsize
|
||||
.shstub ape_elf_phnum,2 # 38: e_phnum
|
||||
.ascii "\\0\\0" # 3a: e_shentsize
|
||||
.shstub ape_elf_shnum,2 # 3c: e_shnum
|
||||
.shstub ape_elf_shstrndx,2 # 3e: e_shstrndx
|
||||
.ascii "' >&7\n"
|
||||
.ascii "exec 7<&-\n"
|
||||
.ascii "else\n"
|
||||
.ascii "exit 121\n"
|
||||
.ascii "fi\n"
|
||||
.ascii "[ x\"$1\" = x--assimilate ] && exit 0\n"
|
||||
#ifndef APE_NO_MODIFY_SELF
|
||||
.ascii "exec \"$0\" \"$@\"\n" # optimistic execution
|
||||
.ascii "exec \"$0\" \"$@\"\n" # try to preserve argv[0]
|
||||
#else
|
||||
.ascii "mv -f \"$o\" \"$d\" 2>/dev/null\n"
|
||||
.ascii "o=\"$d\"\n"
|
||||
.ascii "fi\n"
|
||||
.ascii "}\n"
|
||||
.ascii "o=\"$t\"\n"
|
||||
.ascii "exec \"$o\" \"$@\"\n"
|
||||
#endif /* APE_NO_MODIFY_SELF */
|
||||
.ascii "R=$?\n"
|
||||
.ascii "\n"
|
||||
.ascii "if [ $R -eq 126 ] && [ \"$(uname -m)\" != x86_64 ]; then\n"
|
||||
.ascii "if Q=\"$(command -v qemu-x86_64)\"; then\n"
|
||||
.ascii "if [ \"$(uname -m)\" != x86_64 ]; then\n"
|
||||
.ascii "Q=\"$(command -v qemu-x86_64)\" &&\n"
|
||||
.ascii "exec \"$Q\" \"$o\" \"$@\"\n"
|
||||
.ascii "else\n"
|
||||
.ascii "echo error: need qemu-x86_64 >&2\n"
|
||||
.ascii "fi\n"
|
||||
#ifndef APE_NO_MODIFY_SELF
|
||||
.ascii "elif [ $R -eq 127 ]; then\n" # means argv[0] was wrong
|
||||
.ascii "else\n" # means argv[0] was wrong
|
||||
.ascii " exec \"$o\" \"$@\"\n" # so do a path resolution
|
||||
#endif /* APE_NO_MODIFY_SELF */
|
||||
.ascii "fi\n"
|
||||
|
@ -653,7 +659,14 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
|
|||
.endobj apesh
|
||||
#ifdef APE_LOADER
|
||||
.section .ape.loader,"a",@progbits
|
||||
.align 64
|
||||
ape_loader:
|
||||
.incbin APE_LOADER
|
||||
.endobj ape_loader,globl
|
||||
.align 64
|
||||
ape_loader_end:
|
||||
nop
|
||||
.endobj ape_loader_end,globl
|
||||
.previous
|
||||
#endif /* APE_LOADER */
|
||||
#endif /* SupportsWindows() || SupportsMetal() || SupportsXnu() */
|
||||
|
@ -856,7 +869,7 @@ ape_macho:
|
|||
.long (520f-510f)/4 # count
|
||||
510: .quad 0 # rax
|
||||
.quad IMAGE_BASE_VIRTUAL # rbx
|
||||
.quad 0 # rcx
|
||||
.quad XNU # rcx
|
||||
.quad 0 # rdx
|
||||
.quad 0 # rdi
|
||||
.quad 0 # rsi
|
||||
|
@ -870,7 +883,7 @@ ape_macho:
|
|||
.quad 0 # r13
|
||||
.quad 0 # r14
|
||||
.quad 0 # r15
|
||||
.quad _xnu # rip
|
||||
.quad _start # rip
|
||||
.quad 0 # rflags
|
||||
.quad 0 # cs
|
||||
.quad 0 # fs
|
||||
|
@ -1156,47 +1169,6 @@ gdt: .short 2f-1f # table byte length
|
|||
.quad 0b0000000010101111100100110000000000000000000000001111111111111111 #48
|
||||
2: .endobj gdt,global,hidden
|
||||
|
||||
/*─────────────────────────────────────────────────────────────────────────────╗
|
||||
│ αcτµαlly pδrταblε εxεcµταblε § multiboot stub │
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
boot modernized for the nineties */
|
||||
|
||||
#define GRUB_MAGIC 0x1BADB002
|
||||
#define GRUB_EAX 0x2BADB002
|
||||
#define GRUB_AOUT (1 << 16)
|
||||
#define GRUB_CHECKSUM(FLAGS) (-(GRUB_MAGIC + (FLAGS)) & 0xffffffff)
|
||||
|
||||
// Grub Header.
|
||||
.align 4
|
||||
ape_grub:
|
||||
.long GRUB_MAGIC # Magic
|
||||
.long GRUB_AOUT # Flags
|
||||
.long GRUB_CHECKSUM(GRUB_AOUT) # Checksum
|
||||
.long RVA(ape_grub) # HeaderPhysicalAddress
|
||||
.long IMAGE_BASE_PHYSICAL # TextPhysicalAddress
|
||||
.long PHYSICAL(_edata) # LoadEndPhysicalAddress
|
||||
.long PHYSICAL(_end) # BssEndPhysicalAddress
|
||||
.long RVA(ape_grub_entry) # EntryPhysicalAddress
|
||||
.endobj ape_grub,globl
|
||||
|
||||
// Grub Entrypoint.
|
||||
// Takes CPU out of legacy mode and jumps to normal entrypoint.
|
||||
// @noreturn
|
||||
.align 4
|
||||
ape_grub_entry:
|
||||
.code32
|
||||
// cmp $GRUB_EAX,%eax
|
||||
// jne triplf
|
||||
push $0
|
||||
popf
|
||||
mov $0x40,%dl
|
||||
mov %cr0,%eax
|
||||
and $~CR0_PE,%eax
|
||||
mov %eax,%cr0
|
||||
ljmpw $0,$REAL(pc)
|
||||
.code16
|
||||
.endfn ape_grub_entry
|
||||
|
||||
/*─────────────────────────────────────────────────────────────────────────────╗
|
||||
│ αcτµαlly pδrταblε εxεcµταblε § real mode │
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
|
@ -1473,6 +1445,47 @@ long: push $GDT_LONG_DATA
|
|||
jmp *%rax
|
||||
.endfn long
|
||||
|
||||
/*─────────────────────────────────────────────────────────────────────────────╗
|
||||
│ αcτµαlly pδrταblε εxεcµταblε § multiboot stub │
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
boot modernized for the nineties */
|
||||
|
||||
#define GRUB_MAGIC 0x1BADB002
|
||||
#define GRUB_EAX 0x2BADB002
|
||||
#define GRUB_AOUT (1 << 16)
|
||||
#define GRUB_CHECKSUM(FLAGS) (-(GRUB_MAGIC + (FLAGS)) & 0xffffffff)
|
||||
|
||||
// Grub Header.
|
||||
.align 4
|
||||
ape_grub:
|
||||
.long GRUB_MAGIC # Magic
|
||||
.long GRUB_AOUT # Flags
|
||||
.long GRUB_CHECKSUM(GRUB_AOUT) # Checksum
|
||||
.long RVA(ape_grub) # HeaderPhysicalAddress
|
||||
.long IMAGE_BASE_PHYSICAL # TextPhysicalAddress
|
||||
.long PHYSICAL(_edata) # LoadEndPhysicalAddress
|
||||
.long PHYSICAL(_end) # BssEndPhysicalAddress
|
||||
.long RVA(ape_grub_entry) # EntryPhysicalAddress
|
||||
.endobj ape_grub,globl
|
||||
|
||||
// Grub Entrypoint.
|
||||
// Takes CPU out of legacy mode and jumps to normal entrypoint.
|
||||
// @noreturn
|
||||
.align 4
|
||||
ape_grub_entry:
|
||||
.code32
|
||||
// cmp $GRUB_EAX,%eax
|
||||
// jne triplf
|
||||
push $0
|
||||
popf
|
||||
mov $0x40,%dl
|
||||
mov %cr0,%eax
|
||||
and $~CR0_PE,%eax
|
||||
mov %eax,%cr0
|
||||
ljmpw $0,$REAL(pc)
|
||||
.code64
|
||||
.endfn ape_grub_entry
|
||||
|
||||
/* ▄▄▒▀▀▀▀▒▒▄
|
||||
█████▓▓▄░░░░ ▒▒▄
|
||||
▐█▓▓█▓▄█████▓░ ▀▒▄
|
||||
|
@ -1508,12 +1521,9 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
|||
.byte 0x0f,0x1f,0207 # nop rdi binbase
|
||||
.long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512
|
||||
#endif
|
||||
.weak __hostos
|
||||
ezlea __hostos,ax
|
||||
test %rax,%rax
|
||||
jz 1f
|
||||
movb $METAL,(%rax)
|
||||
1: push $0
|
||||
push $METAL # sets __hostos in crt.S
|
||||
pop %rcx
|
||||
push $0
|
||||
mov %rsp,%rbp
|
||||
mov .Lenv0(%rip),%rax
|
||||
mov %rax,(%rbp) # envp[0][0]
|
||||
|
@ -1529,7 +1539,6 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
|||
push $1 # argc
|
||||
xor %ebp,%ebp
|
||||
xor %eax,%eax
|
||||
xor %ecx,%ecx
|
||||
xor %edx,%edx
|
||||
xor %edi,%edi
|
||||
xor %esi,%esi
|
||||
|
|
34
ape/ape.lds
34
ape/ape.lds
|
@ -243,10 +243,12 @@ SECTIONS {
|
|||
/* Code that needs to be addressable in Real Mode */
|
||||
*(.text.real)
|
||||
KEEP(*(SORT_BY_NAME(.sort.text.real.*)))
|
||||
/* Code we want earlier in the binary w/o modifications */
|
||||
KEEP(*(.ape.loader))
|
||||
HIDDEN(_ereal = .);
|
||||
. += 1;
|
||||
|
||||
/*END: realmode addressability guarantee */
|
||||
/*BEGIN: morphable code */
|
||||
. += 1;
|
||||
|
||||
/* Normal Code */
|
||||
*(.start)
|
||||
|
@ -283,6 +285,7 @@ SECTIONS {
|
|||
/* Privileged code invulnerable to magic */
|
||||
KEEP(*(.ape.pad.privileged));
|
||||
. += . > 0 ? 1 : 0;
|
||||
/*END: morphable code */
|
||||
HIDDEN(__privileged_start = .);
|
||||
. += . > 0 ? 1 : 0;
|
||||
*(.privileged)
|
||||
|
@ -390,15 +393,6 @@ SECTIONS {
|
|||
} :Ram
|
||||
|
||||
/*END: file content that's loaded by o/s */
|
||||
/*BEGIN: payload (for now, only the APE loader) */
|
||||
.payload ALIGN(64) : {
|
||||
/* Loader */
|
||||
HIDDEN(ape_loader = .);
|
||||
KEEP(*(.ape.loader))
|
||||
. = ALIGN(64);
|
||||
HIDDEN(ape_loader_end = .);
|
||||
}
|
||||
/*END: payload */
|
||||
/*BEGIN: bss memory void */
|
||||
|
||||
.zip . : {
|
||||
|
@ -510,7 +504,7 @@ HIDDEN(ape_ram_rva = RVA(ape_ram_vaddr));
|
|||
HIDDEN(ape_stack_pf = DEFINED(ape_stack_pf) ? ape_stack_pf : PF_R | PF_W);
|
||||
HIDDEN(ape_stack_prot = _PF2PROT(ape_stack_pf));
|
||||
HIDDEN(ape_stack_offset = ape_ram_offset + ape_ram_filesz);
|
||||
HIDDEN(ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000 - STACKSIZE);
|
||||
HIDDEN(ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000);
|
||||
HIDDEN(ape_stack_paddr = ape_ram_paddr + ape_ram_filesz);
|
||||
HIDDEN(ape_stack_filesz = 0);
|
||||
HIDDEN(ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : STACKSIZE);
|
||||
|
@ -546,8 +540,14 @@ HIDDEN(ape_bss_filesz = 0);
|
|||
HIDDEN(ape_bss_memsz = SIZEOF(.bss));
|
||||
HIDDEN(ape_bss_align = PAGESIZE);
|
||||
|
||||
SHSTUB2(ape_loader_dd_skip, RVA(ape_loader) / 64);
|
||||
SHSTUB2(ape_loader_dd_count, (ape_loader_end - ape_loader) / 64);
|
||||
/* we roundup here because xnu wants the file load segments page-aligned */
|
||||
/* but we don't want to add the nop padding to the ape program, so we'll */
|
||||
/* let ape.S dd read past the end of the file into the wrapping binaries */
|
||||
SHSTUB2(ape_loader_dd_skip, DEFINED(ape_loader) ? RVA(ape_loader) / 64 : 0);
|
||||
SHSTUB2(ape_loader_dd_count,
|
||||
DEFINED(ape_loader_end)
|
||||
? ROUNDUP(ape_loader_end - ape_loader, PAGESIZE) / 64
|
||||
: 0);
|
||||
|
||||
#if SupportsXnu()
|
||||
SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8);
|
||||
|
@ -697,6 +697,12 @@ ASSERT(DEFINED(_start) || DEFINED(_start16),
|
|||
ASSERT(!DEFINED(_start16) || REAL(_end) < 65536,
|
||||
"ape won't support non-tiny real mode programs");
|
||||
|
||||
ASSERT(IS2POW(ape_stack_memsz),
|
||||
"ape_stack_memsz must be a two power");
|
||||
|
||||
ASSERT(!(ape_stack_vaddr & (ape_stack_memsz - 1)),
|
||||
"ape_stack_vaddr must have ape_stack_memsz alignment; try using STATIC_STACK_ADDR(0x700000000000 - ape_stack_memsz);");
|
||||
|
||||
/* Let's not be like Knight Capital. */
|
||||
/* NOCROSSREFS_TO(.test .text) */
|
||||
|
||||
|
|
106
ape/ape.mk
106
ape/ape.mk
|
@ -15,20 +15,40 @@
|
|||
|
||||
PKGS += APE
|
||||
|
||||
APE = o/$(MODE)/ape/ape.o \
|
||||
APE = o/$(MODE)/ape/ape.o \
|
||||
o/$(MODE)/ape/ape.lds
|
||||
|
||||
APE_NO_MODIFY_SELF = \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
APE_NO_MODIFY_SELF = \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
o/$(MODE)/ape/ape-no-modify-self.o
|
||||
|
||||
APELINK = \
|
||||
$(COMPILE) \
|
||||
-ALINK.ape \
|
||||
$(LINK) \
|
||||
$(LINKARGS) \
|
||||
APE_COPY_SELF = \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
o/$(MODE)/ape/ape-copy-self.o
|
||||
|
||||
APELINK = \
|
||||
$(COMPILE) \
|
||||
-ALINK.ape \
|
||||
$(LINK) \
|
||||
$(LINKARGS) \
|
||||
$(OUTPUT_OPTION)
|
||||
|
||||
APE_LOADER_FLAGS = \
|
||||
-DNDEBUG \
|
||||
-iquote. \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-fpie \
|
||||
-Os \
|
||||
-ffreestanding \
|
||||
-mgeneral-regs-only \
|
||||
-mno-red-zone \
|
||||
-fno-ident \
|
||||
-fno-gnu-unique \
|
||||
-c \
|
||||
$(OUTPUT_OPTION) \
|
||||
$<
|
||||
|
||||
APE_FILES := $(wildcard ape/*.*)
|
||||
APE_HDRS = $(filter %.h,$(APE_FILES))
|
||||
APE_INCS = $(filter %.inc,$(APE_FILES))
|
||||
|
@ -38,32 +58,68 @@ APE_SRCS = $(APE_SRCS_C) $(APE_SRCS_S)
|
|||
APE_OBJS = $(APE_SRCS_S:%.S=o/$(MODE)/%.o)
|
||||
APE_CHECKS = $(APE_HDRS:%=o/%.ok)
|
||||
|
||||
o/$(MODE)/ape/ape.lds: \
|
||||
ape/ape.lds \
|
||||
ape/macros.internal.h \
|
||||
libc/dce.h \
|
||||
o/$(MODE)/ape/ape.lds: \
|
||||
ape/ape.lds \
|
||||
ape/macros.internal.h \
|
||||
libc/dce.h \
|
||||
libc/zip.h
|
||||
|
||||
o/ape/idata.inc: \
|
||||
ape/idata.internal.h \
|
||||
o/ape/idata.inc: \
|
||||
ape/idata.internal.h \
|
||||
ape/relocations.h
|
||||
|
||||
o/$(MODE)/ape/ape-no-modify-self.o: ape/ape.S o/$(MODE)/ape/loader.elf
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -DAPE_LOADER="\"o/$(MODE)/ape/loader.elf\"" -DAPE_NO_MODIFY_SELF $<
|
||||
o/$(MODE)/ape/ape-no-modify-self.o: \
|
||||
ape/ape.S \
|
||||
o/$(MODE)/ape/ape.elf
|
||||
@$(COMPILE) \
|
||||
-AOBJECTIFY.S \
|
||||
$(OBJECTIFY.S) \
|
||||
$(OUTPUT_OPTION) \
|
||||
-DAPE_NO_MODIFY_SELF \
|
||||
-DAPE_LOADER='"o/$(MODE)/ape/ape.elf"' $<
|
||||
|
||||
o/$(MODE)/ape/ape-copy-self.o: \
|
||||
ape/ape.S
|
||||
@$(COMPILE) \
|
||||
-AOBJECTIFY.S \
|
||||
$(OBJECTIFY.S) \
|
||||
$(OUTPUT_OPTION) \
|
||||
-DAPE_NO_MODIFY_SELF $<
|
||||
|
||||
o/$(MODE)/ape/loader.o: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) $(cpp.flags) -fpie -Os -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c $(OUTPUT_OPTION) $<
|
||||
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -g $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-gcc.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) $(cpp.flags) -Os -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c -S $(OUTPUT_OPTION) $<
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-clang.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS)
|
||||
|
||||
o/$(MODE)/ape/loader.elf: \
|
||||
o/$(MODE)/ape/loader.o \
|
||||
o/$(MODE)/ape/loader1.o \
|
||||
o/$(MODE)/ape/loader-xnu.o: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -g $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-xnu-gcc.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS)
|
||||
|
||||
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
|
||||
o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.macho.dbg
|
||||
|
||||
o/$(MODE)/ape/ape.elf.dbg: \
|
||||
o/$(MODE)/ape/loader.o \
|
||||
o/$(MODE)/ape/loader-elf.o \
|
||||
ape/loader.lds
|
||||
@$(ELFLINK) -s -z max-page-size=0x10
|
||||
@$(ELFLINK) -z max-page-size=0x10
|
||||
|
||||
o/$(MODE)/ape/ape.macho.dbg: \
|
||||
o/$(MODE)/ape/loader-xnu.o \
|
||||
o/$(MODE)/ape/loader-macho.o \
|
||||
ape/loader-macho.lds
|
||||
@$(ELFLINK) -z max-page-size=0x10
|
||||
|
||||
.PHONY: o/$(MODE)/ape
|
||||
o/$(MODE)/ape: $(APE) \
|
||||
$(APE_CHECKS) \
|
||||
o/$(MODE)/ape: $(APE_CHECKS) \
|
||||
o/$(MODE)/ape/ape.o \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/ape.macho \
|
||||
o/$(MODE)/ape/ape-copy-self.o \
|
||||
o/$(MODE)/ape/ape-no-modify-self.o
|
||||
|
|
63
ape/apeinstall.sh
Executable file
63
ape/apeinstall.sh
Executable file
|
@ -0,0 +1,63 @@
|
|||
#!/bin/sh
|
||||
|
||||
if ! [ x"$(uname -s)" = xLinux ]; then
|
||||
echo this script is intended for linux binfmt_misc >&2
|
||||
echo freebsd/netbsd/openbsd users can use release binary >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f o/depend ]; then
|
||||
# mkdeps.com build was successfully run so assume we can build
|
||||
echo >&2
|
||||
echo running: make -j8 o//ape/ape.elf >&2
|
||||
make -j8 o//ape/ape.elf || exit
|
||||
echo done >&2
|
||||
else
|
||||
# no evidence we can build, use prebuilt one
|
||||
mkdir -p o//ape || exit
|
||||
cp -af build/bootstrap/ape.elf o//ape/ape.elf
|
||||
fi
|
||||
|
||||
echo >&2
|
||||
echo installing o//ape/ape.elf to /usr/bin/ape >&2
|
||||
echo sudo mv -f o//ape/ape.elf /usr/bin/ape >&2
|
||||
sudo mv -f o//ape/ape.elf /usr/bin/ape || exit
|
||||
echo done >&2
|
||||
|
||||
if [ -e /proc/sys/fs/binfmt_misc/APE ]; then
|
||||
echo >&2
|
||||
echo it looks like APE is already registered with binfmt_misc >&2
|
||||
echo please check that it is mapped to ape not /bin/sh >&2
|
||||
echo cat /proc/sys/fs/binfmt_misc/APE >&2
|
||||
cat /proc/sys/fs/binfmt_misc/APE >&2
|
||||
# TODO: we need better uninstall recommendations
|
||||
# the following works fine for justine
|
||||
# but might remove unrelated software?
|
||||
# sudo sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/status'
|
||||
exit
|
||||
fi
|
||||
|
||||
if ! [ -e /proc/sys/fs/binfmt_misc ]; then
|
||||
echo >&2
|
||||
echo loading binfmt_misc into your kernel >&2
|
||||
echo you may need to edit configs to persist across reboot >&2
|
||||
echo sudo modprobe binfmt_misc >&2
|
||||
sudo modprobe binfmt_misc || exit
|
||||
echo done >&2
|
||||
fi
|
||||
|
||||
if ! [ -e /proc/sys/fs/binfmt_misc/register ]; then
|
||||
echo >&2
|
||||
echo mounting binfmt_misc into your kernel >&2
|
||||
echo you may need to edit configs to persist across reboot >&2
|
||||
echo sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc >&2
|
||||
sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc || exit
|
||||
echo done >&2
|
||||
fi
|
||||
|
||||
echo >&2
|
||||
echo registering APE with binfmt_misc >&2
|
||||
echo you may need to edit configs to persist across reboot >&2
|
||||
echo 'sudo sh -c "echo '"'"':APE:M::MZqFpD::/usr/bin/ape:'"'"' >/proc/sys/fs/binfmt_misc/register"' >&2
|
||||
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" || exit
|
||||
echo done >&2
|
238
ape/loader-elf.S
Normal file
238
ape/loader-elf.S
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 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/elf/def.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/macho.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
// APE Loader Executable Structure
|
||||
// Linux, FreeBSD, NetBSD, OpenBSD
|
||||
|
||||
.align 8
|
||||
ehdr: .ascii "\177ELF"
|
||||
.byte ELFCLASS64
|
||||
.byte ELFDATA2LSB
|
||||
.byte 1
|
||||
.byte ELFOSABI_FREEBSD
|
||||
.quad 0
|
||||
.word ET_EXEC # e_type
|
||||
.word EM_NEXGEN32E # e_machine
|
||||
.long 1 # e_version
|
||||
.quad _start # e_entry
|
||||
.quad phdrs - ehdr # e_phoff
|
||||
.quad 0 # e_shoff
|
||||
.long 0 # e_flags
|
||||
.word 64 # e_ehsize
|
||||
.word 56 # e_phentsize
|
||||
.word 4 # e_phnum
|
||||
.word 0 # e_shentsize
|
||||
.word 0 # e_shnum
|
||||
.word 0 # e_shstrndx
|
||||
.endobj ehdr,globl
|
||||
|
||||
.align 8
|
||||
phdrs: .long PT_LOAD # p_type
|
||||
.long PF_R|PF_X # p_flags
|
||||
.quad 0 # p_offset
|
||||
.quad ehdr # p_vaddr
|
||||
.quad ehdr # p_paddr
|
||||
.quad filesz # p_filesz
|
||||
.quad filesz # p_memsz
|
||||
.quad 64 # p_align
|
||||
|
||||
.long PT_LOAD # p_type
|
||||
.long PF_R|PF_W # p_flags
|
||||
.quad 0 # p_offset
|
||||
.quad bss # p_vaddr
|
||||
.quad bss # p_paddr
|
||||
.quad 0 # p_filesz
|
||||
.quad bsssize # p_memsz
|
||||
.quad 64 # p_align
|
||||
|
||||
.long PT_GNU_STACK # p_type
|
||||
.long PF_R|PF_W # p_flags
|
||||
.quad 0 # p_offset
|
||||
.quad 0 # p_vaddr
|
||||
.quad 0 # p_paddr
|
||||
.quad 0 # p_filesz
|
||||
.quad 0 # p_memsz
|
||||
.quad 16 # p_align
|
||||
|
||||
.long PT_NOTE # p_type
|
||||
.long PF_R # p_flags
|
||||
.quad note - ehdr # p_offset
|
||||
.quad note # p_vaddr
|
||||
.quad note # p_paddr
|
||||
.quad notesize # p_filesz
|
||||
.quad notesize # p_memsz
|
||||
.quad 8 # p_align
|
||||
.endobj phdrs
|
||||
|
||||
note: .long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "OpenBSD"
|
||||
2: .align 4
|
||||
3: .long 0
|
||||
4: .long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "NetBSD"
|
||||
2: .align 4
|
||||
3: .long 901000000
|
||||
4: .endobj note
|
||||
notesize = . - note
|
||||
|
||||
.align 64,0 # for ape.S dd
|
||||
.org 0x180 # for ape.S dd
|
||||
|
||||
// APE Loader XNU Header
|
||||
//
|
||||
// This header is dd'd backwards by the APE shell script when
|
||||
// running on Mac OS X.
|
||||
//
|
||||
// @see ape/ape.S
|
||||
macho: .long 0xFEEDFACE+1
|
||||
.long MAC_CPU_NEXGEN32E
|
||||
.long MAC_CPU_NEXGEN32E_ALL
|
||||
.long MAC_EXECUTE
|
||||
.long 5 # number of load commands
|
||||
.long 60f-10f # size of all load commands
|
||||
.long MAC_NOUNDEFS # flags
|
||||
.long 0 # reserved
|
||||
10: .long MAC_LC_SEGMENT_64
|
||||
.long 20f-10b # unmaps first page dir
|
||||
.ascin "__PAGEZERO",16 # consistent with linux
|
||||
.quad 0,0x200000,0,0 # which forbids mem <2m
|
||||
.long 0,0,0,0
|
||||
20: .long MAC_LC_SEGMENT_64
|
||||
.long 30f-20b
|
||||
.ascin "__TEXT",16
|
||||
.quad ehdr # vaddr
|
||||
.quad 4096 # memsz
|
||||
.quad 0 # file offset
|
||||
.quad filesz # file size
|
||||
.long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot
|
||||
.long PROT_EXEC|PROT_READ # initprot
|
||||
.long 1 # segment section count
|
||||
.long 0 # flags
|
||||
210: .ascin "__text",16 # section name (.text)
|
||||
.ascin "__TEXT",16
|
||||
.quad _start # vaddr
|
||||
.quad textsz # memsz
|
||||
.long textoff # offset
|
||||
.long 6 # align 2**6 = 64
|
||||
.long 0 # reloc table offset
|
||||
.long 0 # relocation count
|
||||
.long MAC_S_ATTR_SOME_INSTRUCTIONS # section type & attributes
|
||||
.long 0,0,0 # reserved
|
||||
30: .long MAC_LC_SEGMENT_64
|
||||
.long 40f-30b
|
||||
.ascin "__DATA",16
|
||||
.quad bss # vaddr
|
||||
.quad bsssize # memsz
|
||||
.quad 0 # offset
|
||||
.quad 0 # file size
|
||||
.long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot
|
||||
.long PROT_READ|PROT_WRITE # initprot
|
||||
.long 1 # segment section count
|
||||
.long 0 # flags
|
||||
310: .ascin "__bss",16 # section name (.bss)
|
||||
.ascin "__DATA",16
|
||||
.quad bss # vaddr
|
||||
.quad bsssize # memsz
|
||||
.long 0 # offset
|
||||
.long 12 # align 2**12 = 4096
|
||||
.long 0 # reloc table offset
|
||||
.long 0 # relocation count
|
||||
.long MAC_S_ZEROFILL # section type & attributes
|
||||
.long 0,0,0 # reserved
|
||||
40: .long MAC_LC_UUID
|
||||
.long 50f-40b
|
||||
.quad 0x3fb29ee4ac6c87aa # uuid1
|
||||
.quad 0xdd2c9bb866d9eef8 # uuid2
|
||||
50: .long MAC_LC_UNIXTHREAD
|
||||
.long 60f-50b # cmdsize
|
||||
.long MAC_THREAD_NEXGEN32E # flavaflav
|
||||
.long (520f-510f)/4 # count
|
||||
510: .quad 0 # rax
|
||||
.quad 0 # rbx
|
||||
.quad 0 # rcx
|
||||
.quad XNU # rdx
|
||||
.quad 0 # rdi
|
||||
.quad 0 # rsi
|
||||
.quad 0 # rbp
|
||||
.quad 0 # rsp
|
||||
.quad 0 # r8
|
||||
.quad 0 # r9
|
||||
.quad 0 # r10
|
||||
.quad 0 # r11
|
||||
.quad 0 # r12
|
||||
.quad 0 # r13
|
||||
.quad 0 # r14
|
||||
.quad 0 # r15
|
||||
.quad _start # rip
|
||||
.quad 0 # rflags
|
||||
.quad 0 # cs
|
||||
.quad 0 # fs
|
||||
.quad 0 # gs
|
||||
520:
|
||||
60:
|
||||
.endobj macho
|
||||
|
||||
.align 64,0 # for ape.S dd
|
||||
.org 0x400 # for ape.S dd
|
||||
|
||||
// Ape Loader Entrpoint
|
||||
//
|
||||
// This is normally called by the operating system. However it may
|
||||
// be called by the Actually Portable Executables themselves, when
|
||||
// re-executing a program. Just do this:
|
||||
//
|
||||
// memcpy(0x200000, loader)
|
||||
// lea handoff(%rip),%rcx
|
||||
// lea argblock(%rip),%rsp
|
||||
// jmp 0x200400
|
||||
//
|
||||
// @see APE_LOADER_ENTRY
|
||||
// @see ape/loader.h
|
||||
_start: mov %rsp,%rsi
|
||||
jmp ApeLoader
|
||||
.endfn _start,globl
|
||||
|
||||
// System Call Entrpoint
|
||||
//
|
||||
// This function is used by the APE loader to make system calls.
|
||||
// We also pass a reference to this function to the APE binary's
|
||||
// _start() function. It's needed because on OpenBSD, msyscall()
|
||||
// restricts which pages can issue system calls, and it can only
|
||||
// be called once. Therefore if we want to be load and re-load a
|
||||
// binary multiple times without calling the system execve(), we
|
||||
// need to be able to handover the SYSCALL function. We hardcode
|
||||
// this to a fixed address, but that shouldn't be used, since we
|
||||
// would ideally want to move it to a random page in the future.
|
||||
__syscall_loader:
|
||||
clc
|
||||
syscall
|
||||
jc 1f
|
||||
ret
|
||||
1: neg %rax
|
||||
ret
|
||||
.endfn __syscall_loader,globl
|
127
ape/loader-macho.S
Normal file
127
ape/loader-macho.S
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 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/macho.internal.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
// APE Loader Executable Structure for XNU
|
||||
|
||||
.align 4096
|
||||
macho: .long 0xFEEDFACE+1
|
||||
.long MAC_CPU_NEXGEN32E
|
||||
.long MAC_CPU_NEXGEN32E_ALL
|
||||
.long MAC_EXECUTE
|
||||
.long 5 # number of load commands
|
||||
.long 60f-10f # size of all load commands
|
||||
.long MAC_NOUNDEFS # flags
|
||||
.long 0 # reserved
|
||||
10: .long MAC_LC_SEGMENT_64
|
||||
.long 20f-10b # unmaps first page dir
|
||||
.ascin "__PAGEZERO",16 # consistent with linux
|
||||
.quad 0,0x200000,0,0 # which forbids mem <2m
|
||||
.long 0,0,0,0
|
||||
20: .long MAC_LC_SEGMENT_64
|
||||
.long 30f-20b
|
||||
.ascin "__TEXT",16
|
||||
.quad macho # vaddr
|
||||
.quad 4096 # memsz
|
||||
.quad 0 # file offset
|
||||
.quad filesz # file size
|
||||
.long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot
|
||||
.long PROT_EXEC|PROT_READ # initprot
|
||||
.long 1 # segment section count
|
||||
.long 0 # flags
|
||||
210: .ascin "__text",16 # section name (.text)
|
||||
.ascin "__TEXT",16
|
||||
.quad _start # vaddr
|
||||
.quad textsz # memsz
|
||||
.long textoff # offset
|
||||
.long 6 # align 2**3 = 64
|
||||
.long 0 # reloc table offset
|
||||
.long 0 # relocation count
|
||||
.long MAC_S_ATTR_SOME_INSTRUCTIONS # section type & attributes
|
||||
.long 0,0,0 # reserved
|
||||
30: .long MAC_LC_SEGMENT_64
|
||||
.long 40f-30b
|
||||
.ascin "__DATA",16
|
||||
.quad bss # vaddr
|
||||
.quad bsssize # memsz
|
||||
.quad 0 # offset
|
||||
.quad 0 # file size
|
||||
.long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot
|
||||
.long PROT_READ|PROT_WRITE # initprot
|
||||
.long 1 # segment section count
|
||||
.long 0 # flags
|
||||
310: .ascin "__bss",16 # section name (.bss)
|
||||
.ascin "__DATA",16
|
||||
.quad bss # vaddr
|
||||
.quad bsssize # memsz
|
||||
.long 0 # offset
|
||||
.long 12 # align 2**12 = 4096
|
||||
.long 0 # reloc table offset
|
||||
.long 0 # relocation count
|
||||
.long MAC_S_ZEROFILL # section type & attributes
|
||||
.long 0,0,0 # reserved
|
||||
40: .long MAC_LC_UUID
|
||||
.long 50f-40b
|
||||
.quad 0x3fb29ee4ac6c87aa # uuid1
|
||||
.quad 0xdd2c9bb866d9eef8 # uuid2
|
||||
50: .long MAC_LC_UNIXTHREAD
|
||||
.long 60f-50b # cmdsize
|
||||
.long MAC_THREAD_NEXGEN32E # flavaflav
|
||||
.long (520f-510f)/4 # count
|
||||
510: .quad 0 # rax
|
||||
.quad 0 # rbx
|
||||
.quad 0 # rcx
|
||||
.quad XNU # rdx
|
||||
.quad 0 # rdi
|
||||
.quad 0 # rsi
|
||||
.quad 0 # rbp
|
||||
.quad 0 # rsp
|
||||
.quad 0 # r8
|
||||
.quad 0 # r9
|
||||
.quad 0 # r10
|
||||
.quad 0 # r11
|
||||
.quad 0 # r12
|
||||
.quad 0 # r13
|
||||
.quad 0 # r14
|
||||
.quad 0 # r15
|
||||
.quad _start # rip
|
||||
.quad 0 # rflags
|
||||
.quad 0 # cs
|
||||
.quad 0 # fs
|
||||
.quad 0 # gs
|
||||
520:
|
||||
60:
|
||||
.endobj macho,globl
|
||||
|
||||
.align 64
|
||||
_start: mov %rsp,%rsi
|
||||
jmp ApeLoader
|
||||
.endfn _start,globl
|
||||
|
||||
__syscall_loader:
|
||||
clc
|
||||
syscall
|
||||
jc 1f
|
||||
ret
|
||||
1: neg %rax
|
||||
ret
|
||||
.endfn __syscall_loader,globl
|
42
ape/loader-macho.lds
Normal file
42
ape/loader-macho.lds
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│
|
||||
│vi: set et sts=2 tw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
. = 0x200000;
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(4096);
|
||||
}
|
||||
filesz = . - macho;
|
||||
textsz = . - _start;
|
||||
.bss ALIGN(4096) : {
|
||||
bss = .;
|
||||
*(.bss)
|
||||
. = ALIGN(4096);
|
||||
}
|
||||
memsz = . - macho;
|
||||
/DISCARD/ : {
|
||||
*(.*)
|
||||
}
|
||||
}
|
||||
|
||||
bsssize = SIZEOF(.bss);
|
||||
textoff = _start - macho;
|
780
ape/loader.c
780
ape/loader.c
|
@ -16,28 +16,88 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/struct/metastat.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/elf/def.h"
|
||||
#include "libc/elf/struct/ehdr.h"
|
||||
#include "libc/elf/struct/phdr.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "ape/loader.h"
|
||||
|
||||
#define TROUBLESHOOT 0
|
||||
#define TROUBLESHOOT_OS LINUX
|
||||
|
||||
/**
|
||||
* @fileoverview APE embeddable loader for Linux and BSD, e.g.
|
||||
* @fileoverview APE Loader for GNU/Systemd/XNU/FreeBSD/NetBSD/OpenBSD
|
||||
*
|
||||
* We recommend using the normal APE design, where binaries assimilate
|
||||
* themselves once by self-modifying the first 64 bytes. If that can't
|
||||
* meet your requirements then we provide an excellent alternative.
|
||||
*
|
||||
* m=tiny
|
||||
* make -j8 MODE=$m o/$m/ape o/$m/examples/printargs.com
|
||||
* o/$m/ape/loader.elf o/$m/examples/printargs.com
|
||||
* o/$m/ape/ape.elf o/$m/examples/printargs.com
|
||||
*
|
||||
* This is an embeddable Actually Portable Executable interpreter. The
|
||||
* `ape/ape.S` bootloader embeds this binary inside each binary that's
|
||||
* linked using `$(APE_NO_MODIFY_SELF)` so it is an automated seamless
|
||||
* process. the shell script at the top of the .COM files will copy it
|
||||
* to `${TMPDIR:-/tmp}/ape` and call execve(). It's a zero copy
|
||||
* operation in praxis since this payload uses mmap() to load the rest
|
||||
* of your executable the same way the kernel does, based on ELF phdrs
|
||||
* which are located in accordance with the first sh printf statement.
|
||||
*
|
||||
* APE executables will look for this program on the system path first
|
||||
* so your APE loader may be installed to your system as follows:
|
||||
*
|
||||
* m=tiny
|
||||
* make -j8 MODE=$m o/$m/ape/ape.elf
|
||||
* sudo cp o/$m/ape/ape.elf /usr/bin/ape
|
||||
*
|
||||
* For Mac OS X systems you should install the `ape.macho` executable:
|
||||
*
|
||||
* sudo cp o/$m/ape/ape.macho /usr/bin/ape
|
||||
*
|
||||
* Your APE loader may be used as a shebang interpreter by doing this:
|
||||
*
|
||||
* #!/usr/bin/ape python.com
|
||||
* # -*- python -*-
|
||||
* print("hello world")
|
||||
*
|
||||
* However you won't need to do that, if your APE Loader is registered
|
||||
* as a binfmt_misc interpreter. You can do that as follows with root:
|
||||
*
|
||||
* sudo cp -f o/$m/ape/ape.elf /usr/bin
|
||||
* f=/proc/sys/fs/binfmt_misc/register
|
||||
* sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >$f"
|
||||
*
|
||||
* If the register file doesn't exist on your Linux machine then you'd
|
||||
* load it using the following commands:
|
||||
*
|
||||
* sudo modprobe binfmt_misc
|
||||
* sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc
|
||||
*
|
||||
* You should now experience a performance boost, and you can also now
|
||||
* use the regular shebang form:
|
||||
*
|
||||
* #!/usr/bin/python.com
|
||||
* # -*- python -*-
|
||||
* print("hello world")
|
||||
*
|
||||
* @note this can probably be used as a binfmt_misc interpreter
|
||||
*/
|
||||
|
||||
#define LINUX 0
|
||||
#define FREEBSD 1
|
||||
#define NETBSD 2
|
||||
#define OPENBSD 3
|
||||
#define LINUX 1
|
||||
#define XNU 8
|
||||
#define OPENBSD 16
|
||||
#define FREEBSD 32
|
||||
#define NETBSD 64
|
||||
|
||||
#define SupportsLinux() (SUPPORT_VECTOR & LINUX)
|
||||
#define SupportsXnu() (SUPPORT_VECTOR & XNU)
|
||||
#define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD)
|
||||
#define SupportsOpenbsd() (SUPPORT_VECTOR & OPENBSD)
|
||||
#define SupportsNetbsd() (SUPPORT_VECTOR & NETBSD)
|
||||
|
||||
#define IsLinux() (SupportsLinux() && os == LINUX)
|
||||
#define IsXnu() (SupportsXnu() && os == XNU)
|
||||
#define IsFreebsd() (SupportsFreebsd() && os == FREEBSD)
|
||||
#define IsOpenbsd() (SupportsOpenbsd() && os == OPENBSD)
|
||||
#define IsNetbsd() (SupportsNetbsd() && os == NETBSD)
|
||||
|
||||
#define O_RDONLY 0
|
||||
#define PROT_READ 1
|
||||
|
@ -46,173 +106,409 @@
|
|||
#define MAP_SHARED 1
|
||||
#define MAP_PRIVATE 2
|
||||
#define MAP_FIXED 16
|
||||
#define MAP_ANONYMOUS (os == LINUX ? 32 : 4096)
|
||||
#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096)
|
||||
#define AT_EXECFN_LINUX 31
|
||||
#define AT_EXECFN_NETBSD 2014
|
||||
#define ELFCLASS64 2
|
||||
#define ELFDATA2LSB 1
|
||||
#define EM_NEXGEN32E 62
|
||||
#define ET_EXEC 2
|
||||
#define PT_LOAD 1
|
||||
#define PT_DYNAMIC 2
|
||||
#define EI_CLASS 4
|
||||
#define EI_DATA 5
|
||||
#define PF_X 1
|
||||
#define PF_W 2
|
||||
#define PF_R 4
|
||||
#define X_OK 1
|
||||
#define XCR0_SSE 2
|
||||
#define XCR0_AVX 4
|
||||
|
||||
#define __NR_read (os == LINUX ? 0 : 3)
|
||||
#define __NR_write (os == LINUX ? 1 : 4)
|
||||
#define __NR_open (os == LINUX ? 2 : 5)
|
||||
#define __NR_close (os == LINUX ? 3 : 6)
|
||||
#define __NR_exit (os == LINUX ? 60 : 1)
|
||||
#define __NR_mmap (os == LINUX ? 9 : os == FREEBSD ? 477 : 197)
|
||||
#define __NR_fstat \
|
||||
(os == LINUX ? 5 : os == FREEBSD ? 551 : os == OPENBSD ? 53 : 440)
|
||||
#define Read32(S) \
|
||||
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
|
||||
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
|
||||
|
||||
static wontreturn void Exit(int os, long rc) {
|
||||
asm volatile("syscall"
|
||||
: /* no outputs */
|
||||
: "a"(__NR_exit), "D"(rc)
|
||||
: "memory");
|
||||
unreachable;
|
||||
#define Read64(S) \
|
||||
((unsigned long)(255 & (S)[7]) << 070 | \
|
||||
(unsigned long)(255 & (S)[6]) << 060 | \
|
||||
(unsigned long)(255 & (S)[5]) << 050 | \
|
||||
(unsigned long)(255 & (S)[4]) << 040 | \
|
||||
(unsigned long)(255 & (S)[3]) << 030 | \
|
||||
(unsigned long)(255 & (S)[2]) << 020 | \
|
||||
(unsigned long)(255 & (S)[1]) << 010 | \
|
||||
(unsigned long)(255 & (S)[0]) << 000)
|
||||
|
||||
struct PathSearcher {
|
||||
char os;
|
||||
unsigned long namelen;
|
||||
const char *name;
|
||||
const char *syspath;
|
||||
char path[1024];
|
||||
};
|
||||
|
||||
struct ElfEhdr {
|
||||
unsigned char e_ident[16];
|
||||
unsigned short e_type;
|
||||
unsigned short e_machine;
|
||||
unsigned e_version;
|
||||
unsigned long e_entry;
|
||||
unsigned long e_phoff;
|
||||
unsigned long e_shoff;
|
||||
unsigned e_flags;
|
||||
unsigned short e_ehsize;
|
||||
unsigned short e_phentsize;
|
||||
unsigned short e_phnum;
|
||||
unsigned short e_shentsize;
|
||||
unsigned short e_shnum;
|
||||
unsigned short e_shstrndx;
|
||||
};
|
||||
|
||||
struct ElfPhdr {
|
||||
unsigned p_type;
|
||||
unsigned p_flags;
|
||||
unsigned long p_offset;
|
||||
unsigned long p_vaddr;
|
||||
unsigned long p_paddr;
|
||||
unsigned long p_filesz;
|
||||
unsigned long p_memsz;
|
||||
unsigned long p_align;
|
||||
};
|
||||
|
||||
static void *syscall;
|
||||
static struct PathSearcher ps;
|
||||
extern char __syscall_loader[];
|
||||
|
||||
static int ToLower(int c) {
|
||||
return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
static void Close(int os, long fd) {
|
||||
long ax, di;
|
||||
asm volatile("syscall"
|
||||
static char *MemCpy(char *d, const char *s, unsigned long n) {
|
||||
unsigned long i = 0;
|
||||
for (; i < n; ++i) d[i] = s[i];
|
||||
return d + n;
|
||||
}
|
||||
|
||||
static unsigned long StrLen(const char *s) {
|
||||
unsigned long n = 0;
|
||||
while (*s++) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
||||
for (; n; --n, ++s) {
|
||||
if ((*s & 255) == c) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *GetEnv(char **p, const char *s) {
|
||||
unsigned long i, j;
|
||||
if (p) {
|
||||
for (i = 0; p[i]; ++i) {
|
||||
for (j = 0;; ++j) {
|
||||
if (!s[j]) {
|
||||
if (p[i][j] == '=') {
|
||||
return p[i] + j + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (s[j] != p[i][j]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *Utoa(char p[21], unsigned long x) {
|
||||
char t;
|
||||
unsigned long i, a, b;
|
||||
i = 0;
|
||||
do {
|
||||
p[i++] = x % 10 + '0';
|
||||
x = x / 10;
|
||||
} while (x > 0);
|
||||
p[i] = '\0';
|
||||
if (i) {
|
||||
for (a = 0, b = i - 1; a < b; ++a, --b) {
|
||||
t = p[a];
|
||||
p[a] = p[b];
|
||||
p[b] = t;
|
||||
}
|
||||
}
|
||||
return p + i;
|
||||
}
|
||||
|
||||
static char *Itoa(char p[21], long x) {
|
||||
if (x < 0) *p++ = '-', x = -(unsigned long)x;
|
||||
return Utoa(p, x);
|
||||
}
|
||||
|
||||
#if TROUBLESHOOT
|
||||
const char *DescribeOs(int os) {
|
||||
if (IsLinux()) {
|
||||
return "GNU/SYSTEMD";
|
||||
} else if (IsXnu()) {
|
||||
return "XNU";
|
||||
} else if (IsFreebsd()) {
|
||||
return "FREEBSD";
|
||||
} else if (IsOpenbsd()) {
|
||||
return "OPENBSD";
|
||||
} else if (IsNetbsd()) {
|
||||
return "NETBSD";
|
||||
} else {
|
||||
return "WUT";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((__noreturn__)) static void Exit(int rc, int os) {
|
||||
asm volatile("call\t*%2"
|
||||
: /* no outputs */
|
||||
: "a"((IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0)), "D"(rc),
|
||||
"rm"(syscall)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void Close(int fd, int os) {
|
||||
int ax, di;
|
||||
asm volatile("call\t*%4"
|
||||
: "=a"(ax), "=D"(di)
|
||||
: "0"(__NR_close), "1"(fd)
|
||||
: "0"((IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
|
||||
"rm"(syscall)
|
||||
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
}
|
||||
|
||||
static long Read(int os, long fd, void *data, unsigned long size) {
|
||||
bool cf;
|
||||
long ax, di, si, dx;
|
||||
asm volatile("clc\n\t"
|
||||
"syscall"
|
||||
: "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "1"(__NR_read), "2"(fd), "3"(data), "4"(size)
|
||||
static int Read(int fd, void *data, int size, int os) {
|
||||
long si;
|
||||
int ax, di, dx;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"((IsLinux() ? 0 : 3) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
|
||||
"2"(data), "3"(size), "rm"(syscall)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||
if (cf) ax = -ax;
|
||||
return ax;
|
||||
}
|
||||
|
||||
static void Write(int os, long fd, const void *data, unsigned long size) {
|
||||
long ax, di, si, dx;
|
||||
asm volatile("syscall"
|
||||
static void Write(int fd, const void *data, int size, int os) {
|
||||
long si;
|
||||
int ax, di, dx;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"(__NR_write), "1"(fd), "2"(data), "3"(size)
|
||||
: "0"((IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
|
||||
"2"(data), "3"(size), "rm"(syscall)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
}
|
||||
|
||||
static long Fstat(int os, long fd, union metastat *st) {
|
||||
long ax, di, si;
|
||||
asm volatile("syscall"
|
||||
: "=a"(ax), "=D"(di), "=S"(si)
|
||||
: "0"(__NR_fstat), "1"(fd), "2"(st)
|
||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
static void Execve(const char *prog, char **argv, char **envp, int os) {
|
||||
long ax, di, si, dx;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"(59 | (IsXnu() ? 0x2000000 : 0)), "1"(prog), "2"(argv),
|
||||
"3"(envp), "rm"(syscall)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
}
|
||||
|
||||
static int Access(const char *path, int mode, int os) {
|
||||
int ax, si;
|
||||
long dx, di;
|
||||
asm volatile("call\t*%7"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"((IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0)),
|
||||
"1"(path), "2"(mode), "rm"(syscall)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
return ax;
|
||||
}
|
||||
|
||||
static void Msyscall(int os, long p, long n) {
|
||||
long ax, di, si;
|
||||
if (os == OPENBSD) {
|
||||
asm volatile("syscall"
|
||||
static int Msyscall(long p, long n, int os) {
|
||||
int ax;
|
||||
long di, si;
|
||||
if (!IsOpenbsd()) {
|
||||
return 0;
|
||||
} else {
|
||||
asm volatile("call\t*%6"
|
||||
: "=a"(ax), "=D"(di), "=S"(si)
|
||||
: "0"(37), "1"(p), "2"(n)
|
||||
: "0"(37), "1"(p), "2"(n), "rm"(syscall)
|
||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
return ax;
|
||||
}
|
||||
}
|
||||
|
||||
static long Open(int os, const char *path, long flags, long mode) {
|
||||
bool cf;
|
||||
long ax, di, si, dx;
|
||||
asm volatile("clc\n\t"
|
||||
"syscall"
|
||||
: "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "1"(__NR_open), "2"(path), "3"(flags), "4"(mode)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||
if (cf) ax = -ax;
|
||||
static int Open(const char *path, int flags, int mode, int os) {
|
||||
long di;
|
||||
int ax, dx, si;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"((IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0)),
|
||||
"1"(path), "2"(flags), "3"(mode), "rm"(syscall)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
return ax;
|
||||
}
|
||||
|
||||
static long Mmap(int os, long addr, long size, long prot, long flags, long fd,
|
||||
long off) {
|
||||
bool cf;
|
||||
long ax;
|
||||
register long flags_ asm("r10") = flags;
|
||||
register long fd_ asm("r8") = fd;
|
||||
__attribute__((__noinline__)) static long Mmap(long addr, long size, int prot,
|
||||
int flags, int fd, long off,
|
||||
int os) {
|
||||
long ax, di, si, dx;
|
||||
register int flags_ asm("r10") = flags;
|
||||
register int fd_ asm("r8") = fd;
|
||||
register long off_ asm("r9") = off;
|
||||
asm volatile("push\t%%r9\n\t"
|
||||
"push\t%%r9\n\t"
|
||||
"clc\n\t"
|
||||
"syscall\n\t"
|
||||
"call\t*%7\n\t"
|
||||
"pop\t%%r9\n\t"
|
||||
"pop\t%%r9"
|
||||
: "=@ccc"(cf), "=a"(ax)
|
||||
: "1"(__NR_mmap), "D"(addr), "S"(size), "d"(prot), "r"(flags_),
|
||||
"r"(fd_), "r"(off_)
|
||||
: "rcx", "r11", "memory");
|
||||
if (cf) ax = -ax;
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(flags_),
|
||||
"+r"(fd_), "+r"(off_)
|
||||
: "rm"(syscall),
|
||||
"0"((IsLinux() ? 9
|
||||
: IsFreebsd() ? 477
|
||||
: 197) |
|
||||
(IsXnu() ? 0x2000000 : 0)),
|
||||
"1"(addr), "2"(size), "3"(prot)
|
||||
: "rcx", "r11", "memory", "cc");
|
||||
return ax;
|
||||
}
|
||||
|
||||
static size_t GetFdSize(int os, int fd) {
|
||||
union metastat st;
|
||||
if (!Fstat(os, fd, &st)) {
|
||||
if (os == LINUX) {
|
||||
return st.linux.st_size;
|
||||
} else if (os == FREEBSD) {
|
||||
return st.freebsd.st_size;
|
||||
} else if (os == OPENBSD) {
|
||||
return st.openbsd.st_size;
|
||||
} else {
|
||||
return st.netbsd.st_size;
|
||||
static void Emit(int os, const char *s) {
|
||||
Write(2, s, StrLen(s), os);
|
||||
}
|
||||
|
||||
static void Perror(int os, const char *c, int rc, const char *s) {
|
||||
char ibuf[21];
|
||||
Emit(os, "ape error: ");
|
||||
Emit(os, c);
|
||||
Emit(os, ": ");
|
||||
Emit(os, s);
|
||||
if (rc) {
|
||||
Emit(os, " failed errno=");
|
||||
Itoa(ibuf, -rc);
|
||||
Emit(os, ibuf);
|
||||
}
|
||||
Emit(os, "\n");
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
|
||||
const char *s) {
|
||||
Perror(os, c, rc, s);
|
||||
Exit(127, os);
|
||||
}
|
||||
|
||||
static int StrCmp(const char *l, const char *r) {
|
||||
unsigned long i = 0;
|
||||
while (l[i] == r[i] && r[i]) ++i;
|
||||
return (l[i] & 255) - (r[i] & 255);
|
||||
}
|
||||
|
||||
static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) {
|
||||
unsigned long i, m;
|
||||
if (n >= (m = StrLen(s))) {
|
||||
for (i = n - m; i < n; ++i) {
|
||||
if (ToLower(p[i]) != *s++) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t Length(const char *s) {
|
||||
size_t n = 0;
|
||||
while (*s++) ++n;
|
||||
return n;
|
||||
static char IsComPath(struct PathSearcher *ps) {
|
||||
return EndsWithIgnoreCase(ps->name, ps->namelen, ".com") ||
|
||||
EndsWithIgnoreCase(ps->name, ps->namelen, ".exe") ||
|
||||
EndsWithIgnoreCase(ps->name, ps->namelen, ".com.dbg");
|
||||
}
|
||||
|
||||
static void Emit(int os, const char *s) {
|
||||
Write(os, 2, s, Length(s));
|
||||
static char AccessCommand(struct PathSearcher *ps, const char *suffix,
|
||||
unsigned long pathlen) {
|
||||
unsigned long suffixlen;
|
||||
suffixlen = StrLen(suffix);
|
||||
if (pathlen + 1 + ps->namelen + suffixlen + 1 > sizeof(ps->path)) return 0;
|
||||
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/';
|
||||
MemCpy(ps->path + pathlen, ps->name, ps->namelen);
|
||||
MemCpy(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1);
|
||||
return !Access(ps->path, X_OK, ps->os);
|
||||
}
|
||||
|
||||
static void Log(int os, const char *s) {
|
||||
#ifndef NDEBUG
|
||||
Emit(os, "ape loader error: ");
|
||||
Emit(os, s);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Spawn(int os, int fd, long *sp, char *b, struct Elf64_Ehdr *e) {
|
||||
size_t i;
|
||||
int prot, flags;
|
||||
long code, codesize;
|
||||
struct Elf64_Phdr *p;
|
||||
if (e->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
Log(os, "EI_CLASS != ELFCLASS64\n");
|
||||
return;
|
||||
static char SearchPath(struct PathSearcher *ps, const char *suffix) {
|
||||
const char *p;
|
||||
unsigned long i;
|
||||
for (p = ps->syspath;;) {
|
||||
for (i = 0; p[i] && p[i] != ':'; ++i) {
|
||||
if (i < sizeof(ps->path)) {
|
||||
ps->path[i] = p[i];
|
||||
}
|
||||
}
|
||||
if (AccessCommand(ps, suffix, i)) {
|
||||
return 1;
|
||||
} else if (p[i] == ':') {
|
||||
p += i + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (e->e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
Log(os, "EI_CLASS != ELFCLASS64\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
||||
if (MemChr(ps->name, '/', ps->namelen) ||
|
||||
MemChr(ps->name, '\\', ps->namelen)) {
|
||||
ps->path[0] = 0;
|
||||
return AccessCommand(ps, suffix, 0);
|
||||
} else {
|
||||
if (AccessCommand(ps, suffix, 0)) return 1;
|
||||
}
|
||||
return SearchPath(ps, suffix);
|
||||
}
|
||||
|
||||
static char *Commandv(struct PathSearcher *ps, int os, const char *name,
|
||||
const char *syspath) {
|
||||
ps->os = os;
|
||||
ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin";
|
||||
if (!(ps->namelen = StrLen((ps->name = name)))) return 0;
|
||||
if (ps->namelen + 1 > sizeof(ps->path)) return 0;
|
||||
if (FindCommand(ps, "") || (!IsComPath(ps) && FindCommand(ps, ".com"))) {
|
||||
return ps->path;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
||||
long *sp, char *page,
|
||||
struct ElfEhdr *e) {
|
||||
long rc;
|
||||
unsigned long i;
|
||||
int prot, flags;
|
||||
struct ElfPhdr *p;
|
||||
long code, codesize;
|
||||
if (e->e_type != ET_EXEC) {
|
||||
Pexit(os, exe, 0, "ELF e_type != ET_EXEC");
|
||||
}
|
||||
if (e->e_machine != EM_NEXGEN32E) {
|
||||
Log(os, "e_machine != EM_NEXGEN32E\n");
|
||||
return;
|
||||
Pexit(os, exe, 0, "ELF e_machine != EM_NEXGEN32E");
|
||||
}
|
||||
if (e->e_type != ET_EXEC) {
|
||||
Log(os, "e_type != ET_EXEC\n");
|
||||
return;
|
||||
if (e->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
Pexit(os, exe, 0, "ELF e_ident[EI_CLASS] != ELFCLASS64");
|
||||
}
|
||||
if (e->e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
Pexit(os, exe, 0, "ELF e_ident[EI_DATA] != ELFDATA2LSB");
|
||||
}
|
||||
if (e->e_phoff + e->e_phnum * sizeof(*p) > 0x1000) {
|
||||
Log(os, "phnum out of bounds\n");
|
||||
return;
|
||||
Pexit(os, exe, 0, "ELF phdrs need to be in first page");
|
||||
}
|
||||
code = 0;
|
||||
codesize = 0;
|
||||
for (p = (struct Elf64_Phdr *)(b + e->e_phoff), i = e->e_phnum; i--;) {
|
||||
for (p = (struct ElfPhdr *)(page + e->e_phoff), i = e->e_phnum; i--;) {
|
||||
if (p[i].p_type == PT_DYNAMIC) {
|
||||
Pexit(os, exe, 0, "not a real executable");
|
||||
}
|
||||
if (p[i].p_type != PT_LOAD) continue;
|
||||
if ((p[i].p_vaddr | p[i].p_filesz | p[i].p_memsz | p[i].p_offset) & 0xfff) {
|
||||
Log(os, "ape requires strict page size padding and alignment\n");
|
||||
return;
|
||||
Pexit(os, exe, 0, "APE phdrs must be 4096-aligned and 4096-padded");
|
||||
}
|
||||
prot = 0;
|
||||
flags = MAP_FIXED | MAP_PRIVATE;
|
||||
|
@ -228,107 +524,211 @@ static void Spawn(int os, int fd, long *sp, char *b, struct Elf64_Ehdr *e) {
|
|||
codesize = p[i].p_filesz;
|
||||
}
|
||||
if (p[i].p_memsz > p[i].p_filesz) {
|
||||
if (Mmap(os, p[i].p_vaddr + p[i].p_filesz, p[i].p_memsz - p[i].p_filesz,
|
||||
prot, flags | MAP_ANONYMOUS, -1, 0) < 0) {
|
||||
Log(os, "bss mmap failed\n");
|
||||
return;
|
||||
if ((rc = Mmap(p[i].p_vaddr + p[i].p_filesz, p[i].p_memsz - p[i].p_filesz,
|
||||
prot, flags | MAP_ANONYMOUS, -1, 0, os)) < 0) {
|
||||
Pexit(os, exe, rc, "bss mmap()");
|
||||
}
|
||||
}
|
||||
if (p[i].p_filesz) {
|
||||
if (Mmap(os, p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
||||
p[i].p_offset) < 0) {
|
||||
Log(os, "image mmap failed\n");
|
||||
return;
|
||||
if ((rc = Mmap(p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
||||
p[i].p_offset, os)) < 0) {
|
||||
Pexit(os, exe, rc, "image mmap()");
|
||||
}
|
||||
}
|
||||
}
|
||||
Close(os, fd);
|
||||
Msyscall(os, code, codesize);
|
||||
sp[1] = sp[0] - 1;
|
||||
++sp;
|
||||
asm volatile("mov\t%2,%%rsp\n\t"
|
||||
"jmpq\t*%1"
|
||||
if (!code) {
|
||||
Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X");
|
||||
}
|
||||
Close(fd, os);
|
||||
|
||||
// authorize only the loaded program to issue system calls. if this
|
||||
// fails, then we pass a link to our syscall function to the program
|
||||
// since it probably means a userspace program executed this loader
|
||||
// and passed us a custom syscall function earlier.
|
||||
if (Msyscall(code, codesize, os) != -1) {
|
||||
syscall = 0;
|
||||
}
|
||||
|
||||
#if TROUBLESHOOT
|
||||
Emit(TROUBLESHOOT_OS, "preparing to jump\n");
|
||||
#endif
|
||||
|
||||
// we clear all the general registers we can to have some wiggle room
|
||||
// to extend the behavior of this loader in the future. we don't need
|
||||
// to clear the xmm registers since the ape loader should be compiled
|
||||
// with the -mgeneral-regs-only flag.
|
||||
register void *r8 asm("r8") = syscall;
|
||||
asm volatile("xor\t%%eax,%%eax\n\t"
|
||||
"xor\t%%r9d,%%r9d\n\t"
|
||||
"xor\t%%r10d,%%r10d\n\t"
|
||||
"xor\t%%r11d,%%r11d\n\t"
|
||||
"xor\t%%ebx,%%ebx\n\t" // netbsd dosen't clear this
|
||||
"xor\t%%r12d,%%r12d\n\t" // netbsd dosen't clear this
|
||||
"xor\t%%r13d,%%r13d\n\t" // netbsd dosen't clear this
|
||||
"xor\t%%r14d,%%r14d\n\t" // netbsd dosen't clear this
|
||||
"xor\t%%r15d,%%r15d\n\t" // netbsd dosen't clear this
|
||||
"mov\t%%rdx,%%rsp\n\t"
|
||||
"xor\t%%edx,%%edx\n\t"
|
||||
"push\t%%rsi\n\t"
|
||||
"xor\t%%esi,%%esi\n\t"
|
||||
"xor\t%%ebp,%%ebp\n\t"
|
||||
"ret"
|
||||
: /* no outputs */
|
||||
: "D"(os == FREEBSD ? sp : 0), "S"(e->e_entry), "d"(sp)
|
||||
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os),
|
||||
"r"(r8)
|
||||
: "memory");
|
||||
unreachable;
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void loader(long di, long *sp) {
|
||||
size_t size;
|
||||
long rc, *auxv;
|
||||
char *p, **argv;
|
||||
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
|
||||
struct ApeLoader *handoff) {
|
||||
int rc;
|
||||
long *auxv;
|
||||
struct ElfEhdr *ehdr;
|
||||
int c, i, fd, os, argc;
|
||||
union {
|
||||
struct Elf64_Ehdr ehdr;
|
||||
char *p, *exe, *prog, **argv, **envp, *page;
|
||||
static union {
|
||||
struct ElfEhdr ehdr;
|
||||
char p[0x1000];
|
||||
} u;
|
||||
os = 0;
|
||||
if (di) {
|
||||
|
||||
// detect freebsd
|
||||
if (handoff) {
|
||||
os = handoff->os;
|
||||
} else if (SupportsFreebsd() && di) {
|
||||
os = FREEBSD;
|
||||
sp = (long *)di;
|
||||
} else if (SupportsXnu() && dl == XNU) {
|
||||
os = XNU;
|
||||
} else {
|
||||
os = 0;
|
||||
}
|
||||
|
||||
// extract arguments
|
||||
argc = *sp;
|
||||
argv = (char **)(sp + 1);
|
||||
auxv = (long *)(argv + argc + 1);
|
||||
envp = (char **)(sp + 1 + argc + 1);
|
||||
auxv = (long *)(sp + 1 + argc + 1);
|
||||
for (;;) {
|
||||
if (!*auxv++) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!auxv[0]) {
|
||||
os = OPENBSD;
|
||||
|
||||
// get syscall function pointer
|
||||
if (handoff && handoff->syscall) {
|
||||
syscall = handoff->syscall;
|
||||
} else {
|
||||
syscall = __syscall_loader;
|
||||
}
|
||||
for (; auxv[0]; auxv += 2) {
|
||||
if (!os) {
|
||||
if (auxv[0] == AT_EXECFN_NETBSD) {
|
||||
os = NETBSD;
|
||||
if (argc > 1) {
|
||||
auxv[1] = (long)argv[1];
|
||||
}
|
||||
} else if (auxv[0] == AT_EXECFN_LINUX) {
|
||||
if (argc > 1) {
|
||||
auxv[1] = (long)argv[1];
|
||||
|
||||
if (handoff) {
|
||||
// we were called by ape_execve()
|
||||
// no argument parsing is needed
|
||||
// no path searching is needed
|
||||
exe = handoff->prog;
|
||||
fd = handoff->fd;
|
||||
exe = handoff->prog;
|
||||
page = handoff->page;
|
||||
ehdr = (struct ElfEhdr *)handoff->page;
|
||||
} else {
|
||||
|
||||
// detect openbsd
|
||||
if (SupportsOpenbsd() && !os && !auxv[0]) {
|
||||
os = OPENBSD;
|
||||
}
|
||||
|
||||
// detect netbsd
|
||||
if (SupportsNetbsd() && !os) {
|
||||
for (; auxv[0]; auxv += 2) {
|
||||
if (auxv[0] == AT_EXECFN_NETBSD) {
|
||||
os = NETBSD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default operating system
|
||||
if (!os) {
|
||||
os = LINUX;
|
||||
}
|
||||
|
||||
// we can load via shell, shebang, or binfmt_misc
|
||||
if (argc >= 3 && !StrCmp(argv[1], "-")) {
|
||||
// if the first argument is a hyphen then we give the user the
|
||||
// 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
|
||||
// it's specified by ANSI X3.159-1988.
|
||||
prog = (char *)sp[3];
|
||||
argc = sp[3] = sp[0] - 3;
|
||||
argv = (char **)((sp += 3) + 1);
|
||||
} else if (argc < 2) {
|
||||
Emit(os, "usage: ape PROG [ARGV1,ARGV2,...]\n"
|
||||
" ape - PROG [ARGV0,ARGV1,...]\n"
|
||||
"αcτµαlly pδrταblε εxεcµταblε loader v1.o\n"
|
||||
"copyright 2022 justine alexandra roberts tunney\n"
|
||||
"https://justine.lol/ape.html\n");
|
||||
Exit(1, os);
|
||||
} else {
|
||||
prog = (char *)sp[2];
|
||||
argc = sp[1] = sp[0] - 1;
|
||||
argv = (char **)((sp += 1) + 1);
|
||||
}
|
||||
|
||||
if (!(exe = Commandv(&ps, os, prog, GetEnv(envp, "PATH")))) {
|
||||
Pexit(os, prog, 0, "not found (maybe chmod +x)");
|
||||
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
|
||||
Pexit(os, exe, fd, "open");
|
||||
} else if ((rc = Read(fd, u.p, sizeof(u.p), os)) < 0) {
|
||||
Pexit(os, exe, rc, "read");
|
||||
} else if (rc != sizeof(u.p) && Read32(u.p) != Read32("\177ELF")) {
|
||||
Pexit(os, exe, 0, "too small");
|
||||
}
|
||||
|
||||
page = u.p;
|
||||
ehdr = &u.ehdr;
|
||||
}
|
||||
if (argc < 2) {
|
||||
Emit(os, "usage: loader PROG [ARGS...]\n");
|
||||
} else if ((fd = Open(os, argv[1], O_RDONLY, 0)) < 0) {
|
||||
Log(os, "open failed\n");
|
||||
} else if ((rc = Read(os, fd, u.p, sizeof(u.p))) < 0) {
|
||||
Log(os, "read failed\n");
|
||||
} else if (rc != sizeof(u.p)) {
|
||||
Log(os, "file too small\n");
|
||||
} else if (READ32LE(u.p) == READ32LE("\177ELF")) {
|
||||
Spawn(os, fd, sp, u.p, &u.ehdr);
|
||||
} else {
|
||||
for (p = u.p; p < u.p + sizeof(u.p); ++p) {
|
||||
if (READ64LE(p) == READ64LE("printf '")) {
|
||||
for (i = 0, p += 8; p + 3 < u.p + sizeof(u.p) && (c = *p++) != '\'';) {
|
||||
if (c == '\\') {
|
||||
|
||||
#if TROUBLESHOOT
|
||||
Emit(TROUBLESHOOT_OS, "os = ");
|
||||
Emit(TROUBLESHOOT_OS, DescribeOs(os));
|
||||
Emit(TROUBLESHOOT_OS, "\n");
|
||||
for (i = 0; i < argc; ++i) {
|
||||
Emit(TROUBLESHOOT_OS, "argv = ");
|
||||
Emit(TROUBLESHOOT_OS, argv[i]);
|
||||
Emit(TROUBLESHOOT_OS, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((IsXnu() && Read32(page) == 0xFEEDFACE + 1) ||
|
||||
(!IsXnu() && Read32(page) == Read32("\177ELF"))) {
|
||||
Close(fd, os);
|
||||
Execve(exe, argv, envp, os);
|
||||
}
|
||||
|
||||
// TODO(jart): Parse Mach-O for old APE binary support on XNU.
|
||||
for (p = page; p < page + sizeof(u.p); ++p) {
|
||||
if (Read64(p) != Read64("printf '")) continue;
|
||||
for (i = 0, p += 8; p + 3 < page + sizeof(u.p) && (c = *p++) != '\'';) {
|
||||
if (c == '\\') {
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c = *p++ - '0';
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c *= 8;
|
||||
c += *p++ - '0';
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c = *p++ - '0';
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c *= 8;
|
||||
c += *p++ - '0';
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c *= 8;
|
||||
c += *p++ - '0';
|
||||
}
|
||||
}
|
||||
c *= 8;
|
||||
c += *p++ - '0';
|
||||
}
|
||||
}
|
||||
u.p[i++] = c;
|
||||
}
|
||||
if (i >= 64 && READ32LE(u.p) == READ32LE("\177ELF")) {
|
||||
Spawn(os, fd, sp, u.p, &u.ehdr);
|
||||
Exit(os, 127);
|
||||
}
|
||||
}
|
||||
page[i++] = c;
|
||||
}
|
||||
if (i >= 64 && Read32(page) == Read32("\177ELF")) {
|
||||
Spawn(os, exe, fd, sp, page, ehdr);
|
||||
}
|
||||
Log(os, "could not find printf elf in first page\n");
|
||||
}
|
||||
Exit(os, 127);
|
||||
|
||||
Pexit(os, exe, 0, "could not find printf elf in first page");
|
||||
}
|
||||
|
|
20
ape/loader.h
Normal file
20
ape/loader.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef COSMOPOLITAN_APE_LOADER_H_
|
||||
#define COSMOPOLITAN_APE_LOADER_H_
|
||||
|
||||
#define APE_LOADER_BASE 0x200000
|
||||
#define APE_LOADER_SIZE 0x200000
|
||||
#define APE_LOADER_ENTRY 0x200400
|
||||
#define APE_LOADER_BSS (PAGESIZE * 2)
|
||||
#define APE_LOADER_STACK 0x7f0000000000
|
||||
#define APE_BLOCK_BASE 0x7e0000000000
|
||||
#define APE_BLOCK_SIZE 0x000200000000
|
||||
|
||||
struct ApeLoader {
|
||||
int fd;
|
||||
int os;
|
||||
char *prog;
|
||||
char *page;
|
||||
void *syscall;
|
||||
};
|
||||
|
||||
#endif /* COSMOPOLITAN_APE_LOADER_H_ */
|
|
@ -17,16 +17,26 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
ENTRY(_start)
|
||||
OUTPUT_FORMAT(binary)
|
||||
|
||||
SECTIONS {
|
||||
. = 0x200000;
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(64);
|
||||
}
|
||||
filesz = . - ehdr;
|
||||
textsz = . - _start;
|
||||
.bss ALIGN(4096) : {
|
||||
bss = .;
|
||||
*(.bss)
|
||||
. = ALIGN(4096);
|
||||
}
|
||||
memsz = . - ehdr;
|
||||
/DISCARD/ : {
|
||||
*(.*)
|
||||
}
|
||||
}
|
||||
|
||||
bsssize = SIZEOF(.bss);
|
||||
textoff = _start - ehdr;
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 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/elf/def.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
// APE Loader Executable Structure
|
||||
// Linux, FreeBSD, NetBSD, OpenBSD
|
||||
|
||||
.align 8
|
||||
ehdr: .ascii "\177ELF"
|
||||
.byte ELFCLASS64
|
||||
.byte ELFDATA2LSB
|
||||
.byte 1
|
||||
.byte ELFOSABI_FREEBSD
|
||||
.quad 0
|
||||
.word ET_EXEC # e_type
|
||||
.word EM_NEXGEN32E # e_machine
|
||||
.long 1 # e_version
|
||||
.quad _start # e_entry
|
||||
.quad phdrs - ehdr # e_phoff
|
||||
.quad 0 # e_shoff
|
||||
.long 0 # e_flags
|
||||
.word 64 # e_ehsize
|
||||
.word 56 # e_phentsize
|
||||
.word 3 # e_phnum
|
||||
.word 0 # e_shentsize
|
||||
.word 0 # e_shnum
|
||||
.word 0 # e_shstrndx
|
||||
.endobj ehdr,globl
|
||||
|
||||
// memcpy(0x200000, loader); xor %eax,%eax; jmp 0x200000
|
||||
jg47h: .org 0x47
|
||||
.endobj jg47h
|
||||
|
||||
_start: mov %rsp,%rsi
|
||||
jmp loader
|
||||
.endfn _start,globl
|
||||
|
||||
.align 8
|
||||
phdrs: .long PT_LOAD # p_type
|
||||
.long PF_R|PF_X # p_flags
|
||||
.quad 0 # p_offset
|
||||
.quad ehdr # p_vaddr
|
||||
.quad ehdr # p_paddr
|
||||
.quad filesz # p_filesz
|
||||
.quad filesz # p_memsz
|
||||
.quad PAGESIZE # p_align
|
||||
.long PT_GNU_STACK # p_type
|
||||
.long PF_R|PF_W # p_flags
|
||||
.quad 0 # p_offset
|
||||
.quad 0 # p_vaddr
|
||||
.quad 0 # p_paddr
|
||||
.quad 0 # p_filesz
|
||||
.quad 0 # p_memsz
|
||||
.quad 16 # p_align
|
||||
.long PT_NOTE # p_type
|
||||
.long PF_R # p_flags
|
||||
.quad note - ehdr # p_offset
|
||||
.quad note # p_vaddr
|
||||
.quad note # p_paddr
|
||||
.quad notesize # p_filesz
|
||||
.quad notesize # p_memsz
|
||||
.quad 8 # p_align
|
||||
.endobj phdrs
|
||||
|
||||
note: .long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "OpenBSD"
|
||||
2: .align 4
|
||||
3: .long 0
|
||||
4: .long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "NetBSD"
|
||||
2: .align 4
|
||||
3: .long 901000000
|
||||
4: .endobj note
|
||||
notesize = . - note
|
BIN
build/bootstrap/ape.elf
Executable file
BIN
build/bootstrap/ape.elf
Executable file
Binary file not shown.
BIN
build/bootstrap/apetest.com
Executable file
BIN
build/bootstrap/apetest.com
Executable file
Binary file not shown.
Binary file not shown.
BIN
build/bootstrap/cocmd.com
Executable file
BIN
build/bootstrap/cocmd.com
Executable file
Binary file not shown.
Binary file not shown.
BIN
build/bootstrap/cp.com
Executable file
BIN
build/bootstrap/cp.com
Executable file
Binary file not shown.
BIN
build/bootstrap/echo.com
Executable file
BIN
build/bootstrap/echo.com
Executable file
Binary file not shown.
BIN
build/bootstrap/gzip.com
Executable file
BIN
build/bootstrap/gzip.com
Executable file
Binary file not shown.
BIN
build/bootstrap/make.com
Executable file
BIN
build/bootstrap/make.com
Executable file
Binary file not shown.
Binary file not shown.
BIN
build/bootstrap/mkdir.com
Executable file
BIN
build/bootstrap/mkdir.com
Executable file
Binary file not shown.
Binary file not shown.
BIN
build/bootstrap/pwd.com
Executable file
BIN
build/bootstrap/pwd.com
Executable file
Binary file not shown.
BIN
build/bootstrap/rm.com
Executable file
BIN
build/bootstrap/rm.com
Executable file
Binary file not shown.
BIN
build/bootstrap/touch.com
Executable file
BIN
build/bootstrap/touch.com
Executable file
Binary file not shown.
BIN
build/bootstrap/unbundle.com
Executable file
BIN
build/bootstrap/unbundle.com
Executable file
Binary file not shown.
Binary file not shown.
|
@ -50,15 +50,42 @@ ARFLAGS = rcsD
|
|||
ZFLAGS ?=
|
||||
XARGS ?= xargs -P4 -rs8000
|
||||
DOT ?= dot
|
||||
GZ ?= gzip
|
||||
CLANG = clang
|
||||
FC = gfortran #/opt/cross9f/bin/x86_64-linux-musl-gfortran
|
||||
TMPDIR = o/tmp
|
||||
|
||||
# see build/compile, etc. which run third_party/gcc/unbundle.sh
|
||||
AR = build/bootstrap/ar.com
|
||||
CP = build/bootstrap/cp.com
|
||||
RM = build/bootstrap/rm.com -f
|
||||
ECHO = build/bootstrap/echo.com
|
||||
TOUCH = build/bootstrap/touch.com
|
||||
PKG = build/bootstrap/package.com
|
||||
MKDEPS = build/bootstrap/mkdeps.com
|
||||
ZIPOBJ = build/bootstrap/zipobj.com
|
||||
MKDIR = build/bootstrap/mkdir.com -p
|
||||
COMPILE = build/bootstrap/compile.com -V9 $(QUOTA)
|
||||
|
||||
COMMA := ,
|
||||
PWD := $(shell build/bootstrap/pwd.com)
|
||||
IMAGE_BASE_VIRTUAL ?= 0x400000
|
||||
|
||||
IGNORE := $(shell $(ECHO) -2 ♥cosmo)
|
||||
IGNORE := $(shell $(MKDIR) o/tmp)
|
||||
|
||||
ifneq ("$(wildcard o/third_party/gcc/bin/x86_64-pc-linux-gnu-as.exe)","")
|
||||
AS = o/third_party/gcc/bin/x86_64-pc-linux-gnu-as.exe
|
||||
CC = o/third_party/gcc/bin/x86_64-pc-linux-gnu-gcc.exe
|
||||
CXX = o/third_party/gcc/bin/x86_64-pc-linux-gnu-g++.exe
|
||||
CXXFILT = o/third_party/gcc/bin/x86_64-pc-linux-gnu-c++filt.exe
|
||||
LD = o/third_party/gcc/bin/x86_64-pc-linux-gnu-ld.bfd.exe
|
||||
NM = o/third_party/gcc/bin/x86_64-pc-linux-gnu-nm.exe
|
||||
GCC = o/third_party/gcc/bin/x86_64-pc-linux-gnu-gcc.exe
|
||||
STRIP = o/third_party/gcc/bin/x86_64-pc-linux-gnu-strip.exe
|
||||
OBJCOPY = o/third_party/gcc/bin/x86_64-pc-linux-gnu-objcopy.exe
|
||||
OBJDUMP = o/third_party/gcc/bin/x86_64-pc-linux-gnu-objdump.exe
|
||||
ADDR2LINE = $(shell build/bootstrap/pwd.com)/o/third_party/gcc/bin/x86_64-pc-linux-gnu-addr2line.exe
|
||||
else
|
||||
IGNORE := $(shell build/bootstrap/unbundle.com)
|
||||
AS = o/third_party/gcc/bin/x86_64-linux-musl-as
|
||||
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
|
||||
CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++
|
||||
|
@ -69,18 +96,12 @@ GCC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
|
|||
STRIP = o/third_party/gcc/bin/x86_64-linux-musl-strip
|
||||
OBJCOPY = o/third_party/gcc/bin/x86_64-linux-musl-objcopy
|
||||
OBJDUMP = o/third_party/gcc/bin/x86_64-linux-musl-objdump
|
||||
ADDR2LINE = $(shell pwd)/o/third_party/gcc/bin/x86_64-linux-musl-addr2line
|
||||
|
||||
COMMA := ,
|
||||
PWD := $(shell pwd)
|
||||
IMAGE_BASE_VIRTUAL ?= 0x400000
|
||||
HELLO := $(shell build/hello)
|
||||
TMPDIR := $(shell build/findtmp)
|
||||
SPAWNER := $(shell build/getcompile) -V$(shell build/getccversion $(CC))
|
||||
COMPILE = $(SPAWNER) $(HARNESSFLAGS) $(QUOTA)
|
||||
ADDR2LINE = $(shell build/bootstrap/pwd.com)/o/third_party/gcc/bin/x86_64-linux-musl-addr2line
|
||||
endif
|
||||
|
||||
export ADDR2LINE
|
||||
export LC_ALL
|
||||
export MKDIR
|
||||
export MODE
|
||||
export SOURCE_DATE_EPOCH
|
||||
export TMPDIR
|
||||
|
@ -115,7 +136,7 @@ TRADITIONAL = \
|
|||
DEFAULT_CCFLAGS = \
|
||||
-Wall \
|
||||
-Werror \
|
||||
-fdebug-prefix-map="$(PWD)"= \
|
||||
-fdebug-prefix-map='$(PWD)'= \
|
||||
-frecord-gcc-switches
|
||||
|
||||
DEFAULT_OFLAGS = \
|
||||
|
@ -180,7 +201,7 @@ PYFLAGS = \
|
|||
ASONLYFLAGS = \
|
||||
-c \
|
||||
-g \
|
||||
--debug-prefix-map="$(PWD)"=
|
||||
--debug-prefix-map='$(PWD)'=
|
||||
|
||||
DEFAULT_LDLIBS =
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/sh
|
||||
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
|
||||
#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
|
||||
#
|
||||
# OVERVIEW
|
||||
#
|
||||
# Temporary Directory Discovery
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# We call this script once per build to ideally find a folder that's
|
||||
# backed by an in-memory file system. We then export it to the TMPDIR
|
||||
# environment variable. Many programs use it under the hood, e.g. gcc,
|
||||
# so it grants many small performance improvements.
|
||||
|
||||
mkdir -p o/tmp
|
||||
echo $PWD/o/tmp
|
|
@ -1,39 +0,0 @@
|
|||
#!/bin/sh
|
||||
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
|
||||
#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
|
||||
#
|
||||
# OVERVIEW
|
||||
#
|
||||
# Compiler Version Discovery
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Cosmopolitan itself may be built using either GCC and Clang, and our
|
||||
# build irons out many of the differences between the two. This script
|
||||
# is used by build/definitions.mk alongside build/getccname to support
|
||||
# the different versions folks use.
|
||||
#
|
||||
# Our aim is to support GCC 4.2.1+ since that's the last GPLv2 version
|
||||
# with any sort of industry consensus. Please note, Cosmopolitan never
|
||||
# links GCC runtimes when using later versions, so some concerns might
|
||||
# not apply.
|
||||
|
||||
if [ ! -d o/third_party/gcc ]; then
|
||||
third_party/gcc/unbundle.sh
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
MAJOR_VERSION=$(
|
||||
$1 --version |
|
||||
head -n1 |
|
||||
sed -n '
|
||||
s!^[^(]*([^)]*) \([[:digit:]][[:digit:]]*\).*!\1!p
|
||||
s!^.*clang.*version \([[:digit:]][[:digit:]]*\).*!\1!p
|
||||
')
|
||||
|
||||
if [ -z "$MAJOR_VERSION" ]; then
|
||||
echo 6
|
||||
else
|
||||
printf '%s\n' "$MAJOR_VERSION"
|
||||
fi
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
|
||||
#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
|
||||
|
||||
if ! [ o/build/bootstrap/compile.com -nt build/bootstrap/compile.com ]; then
|
||||
mkdir -p o/build/bootstrap/
|
||||
cp -f build/bootstrap/compile.com o/build/bootstrap/compile.$$.com
|
||||
o/build/bootstrap/compile.$$.com -n
|
||||
mv -f o/build/bootstrap/compile.$$.com o/build/bootstrap/compile.com
|
||||
fi
|
||||
|
||||
echo o/build/bootstrap/compile.com
|
20
build/hello
20
build/hello
|
@ -1,20 +0,0 @@
|
|||
#!/bin/sh
|
||||
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
|
||||
#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# HELLO BUILD
|
||||
#
|
||||
# OVERVIEW
|
||||
#
|
||||
# We generate a line at the start of each Makefile run, because if we
|
||||
# use `make V=0` mode then the terminal logging trick we use in
|
||||
# tool/build/compile.c will delete the previous line, and we'd rather
|
||||
# have that line not be the bash prompt that ran make.
|
||||
#
|
||||
# This script is also useful for giving us an indicator each time the
|
||||
# build restarts itself from scratch, which can happen in cases like
|
||||
# when dependencies need to be regenerated by tool/build/mkdeps.c
|
||||
|
||||
echo ♥cosmo >&2
|
|
@ -43,6 +43,10 @@
|
|||
# '(progn
|
||||
# (add-hook 'c-mode-common-hook 'jart-c-mode-common-hook)))
|
||||
|
||||
# ctags doesn't understand atomics, e.g.
|
||||
# extern char **environ;
|
||||
set -- --regex-c='/_Atomic(\([^)]*\))/\1/b' "$@"
|
||||
|
||||
# ctags doesn't understand variable prototypes, e.g.
|
||||
# extern char **environ;
|
||||
set -- --regex-c='/^\(\(hidden\|extern\|const\) \)*[_[:alpha:]][_[:alnum:]]*[ *][ *]*\([_[:alpha:]][_[:alnum:]]*[ *][ *]*\)*\([_[:alpha:]][_$[:alnum:]]*\)/\4/b' "$@"
|
||||
|
|
|
@ -29,7 +29,6 @@ o/%.o: %.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx
|
|||
o/%.o: o/%.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $<
|
||||
o/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
|
||||
o/%.inc: %.h ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) -D__ASSEMBLER__ -P $<
|
||||
o/%.pkg: ; @$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
|
||||
o/%.h.ok: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
|
||||
o/%.okk: % ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
o/%.greg.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
|
||||
|
@ -75,7 +74,6 @@ o/$(MODE)/%.ncabi.o: %.ncabi.c ; @$(COMPILE) -AOBJECTIFY.nc $(OBJECTIFY.ncab
|
|||
o/$(MODE)/%.real.o: %.c ; @$(COMPILE) -AOBJECTIFY.real $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%.runs: o/$(MODE)/% ; @$(COMPILE) -ACHECK -tT$@ $< $(TESTARGS)
|
||||
o/$(MODE)/%.pkg: ; @$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
|
||||
o/$(MODE)/%.zip.o: % ; @$(COMPILE) -AZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%-gcc.asm: %.c ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%-gcc.asm: %.cc ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
@ -87,6 +85,14 @@ o/%.a:
|
|||
$(file >$@.args,$^)
|
||||
@$(COMPILE) -AARCHIVE -T$@ $(AR) $(ARFLAGS) $@ @$@.args
|
||||
|
||||
o/%.pkg:
|
||||
$(file >$@.args,$(filter %.o,$^))
|
||||
@$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) @$@.args
|
||||
|
||||
o/$(MODE)/%.pkg:
|
||||
$(file >$@.args,$(filter %.o,$^))
|
||||
@$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) @$@.args
|
||||
|
||||
o/$(MODE)/%.o: %.py o/$(MODE)/third_party/python/pyobj.com
|
||||
@$(COMPILE) -APYOBJ o/$(MODE)/third_party/python/pyobj.com $(PYFLAGS) -o $@ $<
|
||||
|
||||
|
|
|
@ -27,29 +27,38 @@ EOF
|
|||
exit 1
|
||||
fi
|
||||
|
||||
build/sanitycheck2
|
||||
if [ $? -ne 123 ]; then
|
||||
if [ ! -f /proc/sys/fs/binfmt_misc/status ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
STATUS="$(build/bootstrap/apetest.com)"
|
||||
if [ x"$STATUS" != xsuccess ]; then
|
||||
cat <<'EOF' >&2
|
||||
|
||||
ERROR
|
||||
|
||||
Thompson Shell Backwards Compatibility Issue Detected
|
||||
APE Execution error.
|
||||
|
||||
DETAILS
|
||||
|
||||
Actually Portable Executable assumes stock Linux configuration.
|
||||
Normal behavior is non-ELF files with x bit are run by /bin/sh.
|
||||
Linux lets people globally define arbitrary magic interpreters.
|
||||
Your computer couldve been tuned to run MZ scripts inside WINE.
|
||||
So if you use binfmt_misc you need to explicitly register this.
|
||||
Your system has probably been configured to use binfmt_misc. You need
|
||||
to run the command below to install /usr/bin/ape and register it with
|
||||
binfmt_misc. See ape/loader.c for source code, and technical details.
|
||||
|
||||
WORKAROUND
|
||||
|
||||
sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
|
||||
ape/apeinstall.sh
|
||||
|
||||
NOTES
|
||||
|
||||
If it still doesn't work, possibly due to an overly aggressive WINE
|
||||
or WSL registration, or possibly due to the ordering of definitions,
|
||||
then one troubleshooting step is to just unregister everything using
|
||||
sudo sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/status' and try again.
|
||||
|
||||
SEE ALSO
|
||||
|
||||
https://justine.storage.googleapis.com/ape.html
|
||||
https://justine.lol/ape.html
|
||||
|
||||
EOF
|
||||
kill $1
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
MZqFpD=123
|
||||
exit $MZqFpD
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
121
examples/compress.c
Normal file
121
examples/compress.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/assert.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
|
||||
#define CHUNK 32768
|
||||
|
||||
// clang-format off
|
||||
// make -j8 o//examples && dd if=/dev/urandom count=100 | tee a | o//examples/compress.com | o//examples/decompress.com >b && sha1sum a b
|
||||
/*
|
||||
# data file is o/dbg/third_party/python/python.com
|
||||
# level 0 147517 compress 495 MB/s decompress 1.4 GB/s
|
||||
# level 1 80274 compress 29.2 MB/s decompress 303 MB/s
|
||||
# level 2 79384 compress 33.8 MB/s decompress 212 MB/s
|
||||
# level 3 78875 compress 28.9 MB/s decompress 224 MB/s
|
||||
# level 4 78010 compress 27.1 MB/s decompress 319 MB/s <-- sweet spot?
|
||||
# level 5 77107 compress 19.5 MB/s decompress 273 MB/s
|
||||
# level 6 75081 compress 10.0 MB/s decompress 99.3 MB/s
|
||||
# level 7 75022 compress 7.5 MB/s decompress 287 MB/s
|
||||
# level 8 75016 compress 5.4 MB/s decompress 109 MB/s
|
||||
# level 9 75016 compress 5.4 MB/s decompress 344 MB/s
|
||||
m=
|
||||
make -j8 MODE=$m o/$m/examples || exit
|
||||
for level in $(seq 0 9); do
|
||||
o/$m/examples/compress.com $level <o/dbg/third_party/python/python.com | dd count=10000 2>/tmp/info >/tmp/comp
|
||||
compspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info)
|
||||
o/$m/examples/decompress.com $level </tmp/comp | dd count=10000 2>/tmp/info >/dev/null
|
||||
decompspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info)
|
||||
size=$(o/$m/examples/compress.com $level <o/$m/examples/compress.com | wc -c)
|
||||
echo "level $level $size compress $compspeed decompress $decompspeed"
|
||||
done
|
||||
*/
|
||||
// clang-format on
|
||||
|
||||
int compressor(int infd, int outfd, int level) {
|
||||
z_stream zs;
|
||||
int rc, flush;
|
||||
unsigned have;
|
||||
unsigned char *inbuf;
|
||||
unsigned char *outbuf;
|
||||
inbuf = gc(valloc(CHUNK));
|
||||
outbuf = gc(valloc(CHUNK));
|
||||
zs.zalloc = 0;
|
||||
zs.zfree = 0;
|
||||
zs.opaque = 0;
|
||||
rc = deflateInit2(&zs, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (rc != Z_OK) return rc;
|
||||
do {
|
||||
rc = read(infd, inbuf, CHUNK);
|
||||
if (rc == -1) {
|
||||
deflateEnd(&zs);
|
||||
return Z_ERRNO;
|
||||
}
|
||||
zs.avail_in = rc;
|
||||
flush = !rc ? Z_FINISH : Z_SYNC_FLUSH;
|
||||
zs.next_in = inbuf;
|
||||
do {
|
||||
zs.avail_out = CHUNK;
|
||||
zs.next_out = outbuf;
|
||||
rc = deflate(&zs, flush);
|
||||
assert(rc != Z_STREAM_ERROR);
|
||||
have = CHUNK - zs.avail_out;
|
||||
if (write(outfd, outbuf, have) != have) {
|
||||
deflateEnd(&zs);
|
||||
return Z_ERRNO;
|
||||
}
|
||||
} while (!zs.avail_out);
|
||||
assert(!zs.avail_in);
|
||||
} while (flush != Z_FINISH);
|
||||
assert(rc == Z_STREAM_END);
|
||||
deflateEnd(&zs);
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
const char *zerr(int rc) {
|
||||
switch (rc) {
|
||||
case Z_ERRNO:
|
||||
return strerror(errno);
|
||||
case Z_STREAM_ERROR:
|
||||
return "invalid compression level";
|
||||
case Z_DATA_ERROR:
|
||||
return "invalid or incomplete deflate data";
|
||||
case Z_MEM_ERROR:
|
||||
return "out of memory";
|
||||
case Z_VERSION_ERROR:
|
||||
return "zlib version mismatch!";
|
||||
default:
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int rc, level;
|
||||
if (argc > 1) {
|
||||
level = atoi(argv[1]);
|
||||
} else {
|
||||
level = Z_DEFAULT_COMPRESSION;
|
||||
}
|
||||
rc = compressor(0, 1, level);
|
||||
if (rc == Z_OK) {
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "error: compressor: %s\n", zerr(rc));
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -24,22 +25,29 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/dt.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Shell that works on Windows.
|
||||
* @fileoverview Cosmopolitan Shell
|
||||
*
|
||||
* This doesn't have script language features like UNBOURNE.COM but it
|
||||
* works on Windows and, unlike CMD.EXE, actually has CTRL-P and CTRL-R
|
||||
* which alone make it so much better.
|
||||
* works on Windows and, unlike CMD.EXE, has CTRL-P, CTRL-R, and other
|
||||
* GNU Emacs / Readline keyboard shortcuts.
|
||||
*
|
||||
* One day we'll have UNBOURNE.COM working on Windows but the code isn't
|
||||
* very maintainable sadly.
|
||||
*/
|
||||
|
||||
volatile int gotint;
|
||||
|
||||
static void OnInterrupt(int sig) {
|
||||
gotint = sig;
|
||||
}
|
||||
|
||||
static void AddUniqueCompletion(linenoiseCompletions *c, char *s) {
|
||||
size_t i;
|
||||
if (!s) return;
|
||||
|
@ -106,20 +114,40 @@ static char *ShellHint(const char *p, const char **ansi1, const char **ansi2) {
|
|||
return h;
|
||||
}
|
||||
|
||||
static char *MakePrompt(char *p) {
|
||||
char *s, buf[256];
|
||||
if (!gethostname(buf, sizeof(buf))) {
|
||||
p = stpcpy(p, "\e[95m");
|
||||
if ((s = getenv("USER"))) {
|
||||
p = stpcpy(p, s);
|
||||
*p++ = '@';
|
||||
}
|
||||
p = stpcpy(p, buf);
|
||||
*p++ = ':';
|
||||
}
|
||||
p = stpcpy(p, "\e[96m");
|
||||
if ((s = getcwd(buf, sizeof(buf)))) {
|
||||
p = stpcpy(p, s);
|
||||
}
|
||||
return stpcpy(p, "\e[0m>: ");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bool timeit;
|
||||
int64_t nanos;
|
||||
int n, ws, pid;
|
||||
struct rusage ru;
|
||||
struct timespec ts1, ts2;
|
||||
char *prog, path[PATH_MAX];
|
||||
sigset_t chldmask, savemask;
|
||||
struct sigaction ignore, saveint, savequit;
|
||||
char *p, *line, **args, *arg, *start, *state, prompt[64];
|
||||
int stdoutflags, stderrflags;
|
||||
const char *stdoutpath, *stderrpath;
|
||||
int n, rc, ws, pid, child, killcount;
|
||||
struct sigaction sa, saveint, savequit;
|
||||
char *p, *line, **args, *arg, *start, *state, prompt[1024];
|
||||
linenoiseSetFreeHintsCallback(free);
|
||||
linenoiseSetHintsCallback(ShellHint);
|
||||
linenoiseSetCompletionCallback(ShellCompletion);
|
||||
stpcpy(prompt, "$ ");
|
||||
MakePrompt(prompt);
|
||||
while ((line = linenoiseWithHistory(prompt, "cmd"))) {
|
||||
n = 0;
|
||||
start = line;
|
||||
|
@ -129,35 +157,114 @@ int main(int argc, char *argv[]) {
|
|||
} else {
|
||||
timeit = false;
|
||||
}
|
||||
stdoutpath = 0;
|
||||
stderrpath = 0;
|
||||
stdoutflags = 0;
|
||||
stderrflags = 0;
|
||||
args = xcalloc(1, sizeof(*args));
|
||||
while ((arg = strtok_r(start, " \t\r\n", &state))) {
|
||||
args = xrealloc(args, (++n + 1) * sizeof(*args));
|
||||
args[n - 1] = arg;
|
||||
args[n - 0] = 0;
|
||||
// cmd >>stdout.txt
|
||||
if (arg[0] == '>' && arg[1] == '>') {
|
||||
stdoutflags = O_WRONLY | O_APPEND | O_CREAT;
|
||||
stdoutpath = arg + 2;
|
||||
} else if (arg[0] == '>') {
|
||||
// cmd >stdout.txt
|
||||
stdoutflags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
stdoutpath = arg + 1;
|
||||
} else if (arg[0] == '2' && arg[1] == '>' && arg[2] == '>') {
|
||||
// cmd 2>>stderr.txt
|
||||
stderrflags = O_WRONLY | O_APPEND | O_CREAT;
|
||||
stderrpath = arg + 3;
|
||||
} else if (arg[0] == '2' && arg[1] == '>') {
|
||||
// cmd 2>stderr.txt
|
||||
stderrflags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
stderrpath = arg + 2;
|
||||
} else {
|
||||
// arg
|
||||
args = xrealloc(args, (++n + 1) * sizeof(*args));
|
||||
args[n - 1] = arg;
|
||||
args[n - 0] = 0;
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
if (n > 0) {
|
||||
if ((prog = commandv(args[0], path, sizeof(path)))) {
|
||||
ignore.sa_flags = 0;
|
||||
ignore.sa_handler = SIG_IGN;
|
||||
sigemptyset(&ignore.sa_mask);
|
||||
sigaction(SIGINT, &ignore, &saveint);
|
||||
sigaction(SIGQUIT, &ignore, &savequit);
|
||||
|
||||
// let keyboard interrupts kill child and not shell
|
||||
gotint = 0;
|
||||
killcount = 0;
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGQUIT, &sa, &savequit);
|
||||
sa.sa_handler = OnInterrupt;
|
||||
sigaction(SIGINT, &sa, &saveint);
|
||||
sigemptyset(&chldmask);
|
||||
sigaddset(&chldmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
||||
|
||||
// record timestamp
|
||||
if (timeit) {
|
||||
clock_gettime(CLOCK_REALTIME, &ts1);
|
||||
}
|
||||
if (!fork()) {
|
||||
|
||||
// launch process
|
||||
if (!(child = vfork())) {
|
||||
if (stdoutpath) {
|
||||
close(1);
|
||||
open(stdoutpath, stdoutflags, 0644);
|
||||
}
|
||||
if (stderrpath) {
|
||||
close(2);
|
||||
open(stderrpath, stderrflags, 0644);
|
||||
}
|
||||
sigaction(SIGINT, &saveint, 0);
|
||||
sigaction(SIGQUIT, &savequit, 0);
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
execv(prog, args);
|
||||
_Exit(127);
|
||||
}
|
||||
wait4(0, &ws, 0, &ru);
|
||||
|
||||
// wait for process
|
||||
for (;;) {
|
||||
if (gotint) {
|
||||
switch (killcount) {
|
||||
case 0:
|
||||
// ctrl-c
|
||||
// we do nothing
|
||||
// terminals broadcast sigint to process group
|
||||
rc = 0;
|
||||
break;
|
||||
case 1:
|
||||
// ctrl-c ctrl-c
|
||||
// we try sending sigterm
|
||||
rc = kill(child, SIGTERM);
|
||||
break;
|
||||
default:
|
||||
// ctrl-c ctrl-c ctrl-c ...
|
||||
// we use kill -9 as our last resort
|
||||
rc = kill(child, SIGKILL);
|
||||
break;
|
||||
}
|
||||
if (rc == -1) {
|
||||
fprintf(stderr, "kill failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
++killcount;
|
||||
gotint = 0;
|
||||
}
|
||||
rc = wait4(0, &ws, 0, &ru);
|
||||
if (rc != -1) {
|
||||
break;
|
||||
} else if (errno == EINTR) {
|
||||
errno = 0;
|
||||
} else {
|
||||
fprintf(stderr, "wait failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// print resource consumption for `time` pseudocommand
|
||||
if (timeit) {
|
||||
clock_gettime(CLOCK_REALTIME, &ts2);
|
||||
if (ts2.tv_sec == ts1.tv_sec) {
|
||||
|
@ -174,23 +281,27 @@ int main(int argc, char *argv[]) {
|
|||
free(p);
|
||||
}
|
||||
|
||||
// update prompt to reflect exit status
|
||||
p = prompt;
|
||||
if (WIFEXITED(ws)) {
|
||||
if (WEXITSTATUS(ws)) {
|
||||
if (!__nocolor) p = stpcpy(p, "\e[1;31m");
|
||||
p = stpcpy(p, "rc=");
|
||||
p = FormatInt32(p, WEXITSTATUS(ws));
|
||||
if (128 < WEXITSTATUS(ws) && WEXITSTATUS(ws) <= 128 + 32) {
|
||||
*p++ = ' ';
|
||||
p = stpcpy(p, strsignal(WEXITSTATUS(ws) - 128));
|
||||
}
|
||||
if (!__nocolor) p = stpcpy(p, "\e[0m");
|
||||
*p++ = ' ';
|
||||
}
|
||||
} else {
|
||||
if (!__nocolor) p = stpcpy(p, "\e[1;31m");
|
||||
p = stpcpy(p, "rc=");
|
||||
p = stpcpy(p, strsignal(WTERMSIG(ws)));
|
||||
if (!__nocolor) p = stpcpy(p, "\e[0m");
|
||||
*p++ = ' ';
|
||||
}
|
||||
p = stpcpy(p, "$ ");
|
||||
MakePrompt(p);
|
||||
|
||||
sigaction(SIGINT, &saveint, 0);
|
||||
sigaction(SIGQUIT, &savequit, 0);
|
|
@ -1,96 +0,0 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/copyfile.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
|
||||
#define USAGE \
|
||||
" SRC... DST\n\
|
||||
\n\
|
||||
SYNOPSIS\n\
|
||||
\n\
|
||||
Copies Files\n\
|
||||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-?\n\
|
||||
-h help\n\
|
||||
-f force\n\
|
||||
-n no clobber\n\
|
||||
-a preserve all\n\
|
||||
-p preserve owner and timestamps\n\
|
||||
\n"
|
||||
|
||||
int flags;
|
||||
bool force;
|
||||
|
||||
wontreturn void PrintUsage(int rc, FILE *f) {
|
||||
fprintf(f, "%s%s%s", "Usage: ", program_invocation_name, USAGE);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "?hfnap")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
force = true;
|
||||
break;
|
||||
case 'n':
|
||||
flags |= COPYFILE_NOCLOBBER;
|
||||
break;
|
||||
case 'a':
|
||||
case 'p':
|
||||
flags |= COPYFILE_PRESERVE_OWNER;
|
||||
flags |= COPYFILE_PRESERVE_TIMESTAMPS;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cp(const char *src, const char *dst) {
|
||||
if (endswith(dst, "/") || isdirectory(dst)) {
|
||||
dst = _gc(xasprintf("%s/%s", dst, basename(src)));
|
||||
}
|
||||
if (!force && access(dst, W_OK) == -1 && errno != ENOENT) goto OnFail;
|
||||
if (copyfile(src, dst, flags) == -1) goto OnFail;
|
||||
return 0;
|
||||
OnFail:
|
||||
fprintf(stderr, "%s %s %s: %s\n", "error: cp", src, dst, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
GetOpts(argc, argv);
|
||||
if (argc - optind < 2) PrintUsage(EX_USAGE, stderr);
|
||||
for (i = optind; i < argc - 1; ++i) {
|
||||
if (cp(argv[i], argv[argc - 1]) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
|
||||
/**
|
||||
* @fileoverview How to print backtraces and cpu state on crash.
|
||||
|
|
123
examples/decompress.c
Normal file
123
examples/decompress.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/assert.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
|
||||
#define CHUNK 32768
|
||||
|
||||
// clang-format off
|
||||
// make -j8 o//examples && dd if=/dev/urandom count=100 | tee a | o//examples/compress.com | o//examples/decompress.com >b && sha1sum a b
|
||||
/*
|
||||
# data file is o/dbg/third_party/python/python.com
|
||||
# level 0 147517 compress 495 MB/s decompress 1.4 GB/s
|
||||
# level 1 80274 compress 29.2 MB/s decompress 303 MB/s
|
||||
# level 2 79384 compress 33.8 MB/s decompress 212 MB/s
|
||||
# level 3 78875 compress 28.9 MB/s decompress 224 MB/s
|
||||
# level 4 78010 compress 27.1 MB/s decompress 319 MB/s <-- sweet spot?
|
||||
# level 5 77107 compress 19.5 MB/s decompress 273 MB/s
|
||||
# level 6 75081 compress 10.0 MB/s decompress 99.3 MB/s
|
||||
# level 7 75022 compress 7.5 MB/s decompress 287 MB/s
|
||||
# level 8 75016 compress 5.4 MB/s decompress 109 MB/s
|
||||
# level 9 75016 compress 5.4 MB/s decompress 344 MB/s
|
||||
m=
|
||||
make -j8 MODE=$m o/$m/examples || exit
|
||||
for level in $(seq 0 9); do
|
||||
o/$m/examples/compress.com $level <o/dbg/third_party/python/python.com | dd count=10000 2>/tmp/info >/tmp/comp
|
||||
compspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info)
|
||||
o/$m/examples/decompress.com $level </tmp/comp | dd count=10000 2>/tmp/info >/dev/null
|
||||
decompspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info)
|
||||
size=$(o/$m/examples/compress.com $level <o/$m/examples/compress.com | wc -c)
|
||||
echo "level $level $size compress $compspeed decompress $decompspeed"
|
||||
done
|
||||
*/
|
||||
// clang-format on
|
||||
|
||||
int decompressor(int infd, int outfd) {
|
||||
int rc;
|
||||
unsigned have;
|
||||
z_stream zs;
|
||||
unsigned char *inbuf;
|
||||
unsigned char *outbuf;
|
||||
inbuf = gc(valloc(CHUNK));
|
||||
outbuf = gc(valloc(CHUNK));
|
||||
zs.zalloc = Z_NULL;
|
||||
zs.zfree = Z_NULL;
|
||||
zs.opaque = Z_NULL;
|
||||
zs.avail_in = 0;
|
||||
zs.next_in = Z_NULL;
|
||||
rc = inflateInit(&zs);
|
||||
if (rc != Z_OK) return rc;
|
||||
do {
|
||||
rc = read(infd, inbuf, CHUNK);
|
||||
if (rc == -1) {
|
||||
inflateEnd(&zs);
|
||||
return Z_ERRNO;
|
||||
}
|
||||
if (!rc) {
|
||||
break;
|
||||
}
|
||||
zs.avail_in = rc;
|
||||
zs.next_in = inbuf;
|
||||
do {
|
||||
zs.avail_out = CHUNK;
|
||||
zs.next_out = outbuf;
|
||||
rc = inflate(&zs, Z_SYNC_FLUSH);
|
||||
assert(rc != Z_STREAM_ERROR);
|
||||
switch (rc) {
|
||||
case Z_NEED_DICT:
|
||||
rc = Z_DATA_ERROR;
|
||||
// fallthrough
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
inflateEnd(&zs);
|
||||
return rc;
|
||||
}
|
||||
have = CHUNK - zs.avail_out;
|
||||
if (write(outfd, outbuf, have) != have) {
|
||||
inflateEnd(&zs);
|
||||
return Z_ERRNO;
|
||||
}
|
||||
} while (!zs.avail_out);
|
||||
} while (rc != Z_STREAM_END);
|
||||
inflateEnd(&zs);
|
||||
return rc == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
|
||||
}
|
||||
|
||||
const char *zerr(int rc) {
|
||||
switch (rc) {
|
||||
case Z_ERRNO:
|
||||
return strerror(errno);
|
||||
case Z_STREAM_ERROR:
|
||||
return "invalid compression level";
|
||||
case Z_DATA_ERROR:
|
||||
return "invalid or incomplete deflate data";
|
||||
case Z_MEM_ERROR:
|
||||
return "out of memory";
|
||||
case Z_VERSION_ERROR:
|
||||
return "zlib version mismatch!";
|
||||
default:
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int rc;
|
||||
rc = decompressor(0, 1);
|
||||
if (rc == Z_OK) {
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "error: decompressor: %s\n", zerr(rc));
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,12 @@
|
|||
|
||||
PKGS += EXAMPLES
|
||||
|
||||
ifeq ($(MODE),tiny)
|
||||
EXAMPLES_BOOTLOADER = $(CRT) $(APE)
|
||||
else
|
||||
EXAMPLES_BOOTLOADER = $(CRT) $(APE_NO_MODIFY_SELF)
|
||||
endif
|
||||
|
||||
EXAMPLES_FILES := $(wildcard examples/*)
|
||||
EXAMPLES_MAINS_S = $(filter %.S,$(EXAMPLES_FILES))
|
||||
EXAMPLES_MAINS_C = $(filter %.c,$(EXAMPLES_FILES))
|
||||
|
@ -100,8 +106,15 @@ o/$(MODE)/examples/%.com.dbg: \
|
|||
$(EXAMPLES_DEPS) \
|
||||
o/$(MODE)/examples/%.o \
|
||||
o/$(MODE)/examples/examples.pkg \
|
||||
$(EXAMPLES_BOOTLOADER)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/nomodifyself.com.dbg: \
|
||||
$(EXAMPLES_DEPS) \
|
||||
o/$(MODE)/examples/nomodifyself.o \
|
||||
o/$(MODE)/examples/examples.pkg \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/hellolua.com.dbg: \
|
||||
|
@ -109,8 +122,7 @@ o/$(MODE)/examples/hellolua.com.dbg: \
|
|||
o/$(MODE)/examples/hellolua.o \
|
||||
o/$(MODE)/examples/hellolua.lua.zip.o \
|
||||
o/$(MODE)/examples/examples.pkg \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
$(EXAMPLES_BOOTLOADER)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/ispell.com.dbg: \
|
||||
|
@ -118,8 +130,7 @@ o/$(MODE)/examples/ispell.com.dbg: \
|
|||
o/$(MODE)/examples/ispell.o \
|
||||
o/$(MODE)/usr/share/dict/words.zip.o \
|
||||
o/$(MODE)/examples/examples.pkg \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
$(EXAMPLES_BOOTLOADER)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/nesemu1.com.dbg: \
|
||||
|
@ -129,8 +140,7 @@ o/$(MODE)/examples/nesemu1.com.dbg: \
|
|||
o/$(MODE)/usr/share/rom/zelda.nes.zip.o \
|
||||
o/$(MODE)/usr/share/rom/tetris.nes.zip.o \
|
||||
o/$(MODE)/examples/examples.pkg \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
$(EXAMPLES_BOOTLOADER)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/nesemu1.com: \
|
||||
|
@ -143,23 +153,19 @@ o/$(MODE)/examples/nesemu1.com: \
|
|||
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
|
||||
o/$(MODE)/examples/.nesemu1/.symtab
|
||||
|
||||
o/$(MODE)/examples/hello.com.dbg: \
|
||||
$(EXAMPLES_DEPS) \
|
||||
o/$(MODE)/examples/hello.o \
|
||||
o/$(MODE)/examples/examples.pkg \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/nesemu1.o: QUOTA += -M512m
|
||||
o/$(MODE)/usr/share/dict/words.zip.o: ZIPOBJ_FLAGS += -C2
|
||||
|
||||
$(EXAMPLES_OBJS): examples/examples.mk
|
||||
|
||||
usr/share/dict/words: usr/share/dict/words.gz
|
||||
@mkdir -p $(@D)
|
||||
@$(GZ) $(ZFLAGS) -d <$< >$@
|
||||
o/$(MODE)/usr/share/dict/words: \
|
||||
usr/share/dict/words.gz \
|
||||
o/$(MODE)/tool/build/gzip.com
|
||||
@$(MKDIR) $(@D)
|
||||
@o/$(MODE)/tool/build/gzip.com $(ZFLAGS) -cd <$< >$@
|
||||
|
||||
.PHONY: o/$(MODE)/examples
|
||||
o/$(MODE)/examples: \
|
||||
o/$(MODE)/examples/package \
|
||||
o/$(MODE)/examples/pyapp \
|
||||
$(EXAMPLES_BINS)
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
/*-*- 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 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/sock/goodsocket.internal.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
|
@ -34,6 +34,7 @@
|
|||
#include "libc/sysv/consts/clone.h"
|
||||
#include "libc/sysv/consts/ipproto.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/rlimit.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
@ -69,25 +70,25 @@
|
|||
* Like redbean, greenbean has superior performance too, with an
|
||||
* advantage on benchmarks biased towards high connection counts
|
||||
*
|
||||
* $ sudo wrk -c 300 -t 32 --latency http://127.0.0.1:8080/
|
||||
* Running 10s test @ http://127.0.0.1:8080/
|
||||
* $ wrk -c 300 -t 32 --latency http://10.10.10.124:8080/
|
||||
* Running 10s test @ http://10.10.10.124:8080/
|
||||
* 32 threads and 300 connections
|
||||
* Thread Stats Avg Stdev Max +/- Stdev
|
||||
* Latency 36.21us 133.39us 8.10ms 98.52%
|
||||
* Req/Sec 73.24k 28.92k 131.06k 47.49%
|
||||
* Latency 661.06us 5.11ms 96.22ms 98.85%
|
||||
* Req/Sec 42.38k 8.90k 90.47k 84.65%
|
||||
* Latency Distribution
|
||||
* 50% 22.00us
|
||||
* 75% 29.00us
|
||||
* 90% 40.00us
|
||||
* 99% 333.00us
|
||||
* 4356560 requests in 4.62s, 1.29GB read
|
||||
* Requests/sec: 942663.73
|
||||
* Transfer/sec: 284.98MB
|
||||
* 50% 184.00us
|
||||
* 75% 201.00us
|
||||
* 90% 224.00us
|
||||
* 99% 11.99ms
|
||||
* 10221978 requests in 7.60s, 3.02GB read
|
||||
* Requests/sec: 1345015.69
|
||||
* Transfer/sec: 406.62MB
|
||||
*
|
||||
*/
|
||||
|
||||
#define THREADS 32
|
||||
#define HEARTBEAT 500
|
||||
#define PORT 8080
|
||||
#define HEARTBEAT 100
|
||||
#define KEEPALIVE 5000
|
||||
#define LOGGING 0
|
||||
|
||||
|
@ -96,40 +97,48 @@
|
|||
"Referrer-Policy: origin\r\n" \
|
||||
"Cache-Control: private; max-age=0\r\n"
|
||||
|
||||
int workers;
|
||||
int barrier;
|
||||
int closingtime;
|
||||
int threads;
|
||||
_Atomic(int) workers;
|
||||
_Atomic(int) messages;
|
||||
_Atomic(int) listening;
|
||||
_Atomic(int) connections;
|
||||
_Atomic(int) closingtime;
|
||||
const char *volatile status;
|
||||
|
||||
int Worker(void *id) {
|
||||
int server, itsover, ready, yes = 1;
|
||||
|
||||
// announce to the main process this has spawned
|
||||
kprintf(" #%.2ld", (intptr_t)id);
|
||||
__atomic_add_fetch(&workers, 1, __ATOMIC_SEQ_CST);
|
||||
|
||||
// wait for all threads to spawn before we proceed
|
||||
for (;;) {
|
||||
__atomic_load(&barrier, &ready, __ATOMIC_SEQ_CST);
|
||||
if (ready) break;
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
int server, yes = 1;
|
||||
|
||||
// load balance incoming connections for port 8080 across all threads
|
||||
// hangup on any browser clients that lag for more than a few seconds
|
||||
struct timeval timeo = {KEEPALIVE / 1000, KEEPALIVE % 1000};
|
||||
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(8080)};
|
||||
CHECK_NE(-1, (server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)));
|
||||
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(PORT)};
|
||||
|
||||
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (server == -1) {
|
||||
kprintf("socket() failed %m\n"
|
||||
" try running: sudo prlimit --pid=$$ --nofile=%d\n",
|
||||
threads * 2);
|
||||
goto WorkerFinished;
|
||||
}
|
||||
|
||||
setsockopt(server, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
|
||||
setsockopt(server, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
|
||||
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
setsockopt(server, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
|
||||
setsockopt(server, SOL_TCP, TCP_FASTOPEN, &yes, sizeof(yes));
|
||||
setsockopt(server, SOL_TCP, TCP_QUICKACK, &yes, sizeof(yes));
|
||||
CHECK_EQ(0, bind(server, &addr, sizeof(addr)));
|
||||
CHECK_EQ(0, listen(server, 10));
|
||||
errno = 0;
|
||||
|
||||
if (bind(server, &addr, sizeof(addr)) == -1) {
|
||||
kprintf("%s() failed %m\n", "socket");
|
||||
goto CloseWorker;
|
||||
}
|
||||
|
||||
listen(server, 1);
|
||||
|
||||
// connection loop
|
||||
for (;;) {
|
||||
++listening;
|
||||
while (!closingtime) {
|
||||
struct tm tm;
|
||||
int64_t unixts;
|
||||
struct Url url;
|
||||
|
@ -141,6 +150,13 @@ int Worker(void *id) {
|
|||
char inbuf[1500], outbuf[512], *p, *q;
|
||||
int clientip, client, inmsglen, outmsglen;
|
||||
|
||||
// this slows the server down a lot but is needed on non-Linux to
|
||||
// react to keyboard ctrl-c
|
||||
if (!IsLinux() &&
|
||||
poll(&(struct pollfd){server, POLLIN}, 1, HEARTBEAT) < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// wait for client connection
|
||||
clientaddrsize = sizeof(clientaddr);
|
||||
client = accept(server, &clientaddr, &clientaddrsize);
|
||||
|
@ -153,12 +169,12 @@ int Worker(void *id) {
|
|||
// inherited by the accepted sockets, but using them also has the
|
||||
// side-effect that the listening socket fails with EAGAIN, every
|
||||
// several seconds. we can use that to our advantage to check for
|
||||
// the ctrl-c shutdown event; otherwise, we retry the accept call
|
||||
__atomic_load(&closingtime, &itsover, __ATOMIC_SEQ_CST);
|
||||
if (itsover) break;
|
||||
// the ctrl-c shutdowne event; otherwise, we retry the accept call
|
||||
continue;
|
||||
}
|
||||
|
||||
++connections;
|
||||
|
||||
// message loop
|
||||
do {
|
||||
// parse the incoming http message
|
||||
|
@ -167,11 +183,12 @@ int Worker(void *id) {
|
|||
if ((got = read(client, inbuf, sizeof(inbuf))) <= 0) break;
|
||||
// check that client message wasn't fragmented into more reads
|
||||
if (!(inmsglen = ParseHttpMessage(&msg, inbuf, got))) break;
|
||||
++messages;
|
||||
|
||||
#if LOGGING
|
||||
// log the incoming http message
|
||||
clientip = ntohl(clientaddr.sin_addr.s_addr);
|
||||
kprintf("#%.2ld get some %d.%d.%d.%d:%d %#.*s\n", (intptr_t)id,
|
||||
kprintf("%6P get some %d.%d.%d.%d:%d %#.*s\n",
|
||||
(clientip & 0xff000000) >> 030, (clientip & 0x00ff0000) >> 020,
|
||||
(clientip & 0x0000ff00) >> 010, (clientip & 0x000000ff) >> 000,
|
||||
ntohs(clientaddr.sin_port), msg.uri.b - msg.uri.a,
|
||||
|
@ -189,7 +206,7 @@ int Worker(void *id) {
|
|||
p = stpcpy(outbuf, "HTTP/1.1 200 OK\r\n" STANDARD_RESPONSE_HEADERS
|
||||
"Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Date: ");
|
||||
clock_gettime(CLOCK_REALTIME, &ts), unixts = ts.tv_sec;
|
||||
clock_gettime(0, &ts), unixts = ts.tv_sec;
|
||||
p = FormatHttpDateTime(p, gmtime_r(&unixts, &tm));
|
||||
p = stpcpy(p, "\r\nContent-Length: ");
|
||||
p = FormatInt32(p, strlen(q));
|
||||
|
@ -207,7 +224,7 @@ int Worker(void *id) {
|
|||
"HTTP/1.1 404 Not Found\r\n" STANDARD_RESPONSE_HEADERS
|
||||
"Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Date: ");
|
||||
clock_gettime(CLOCK_REALTIME, &ts), unixts = ts.tv_sec;
|
||||
clock_gettime(0, &ts), unixts = ts.tv_sec;
|
||||
p = FormatHttpDateTime(p, gmtime_r(&unixts, &tm));
|
||||
p = stpcpy(p, "\r\nContent-Length: ");
|
||||
p = FormatInt32(p, strlen(q));
|
||||
|
@ -227,58 +244,101 @@ int Worker(void *id) {
|
|||
(msg.method == kHttpGet || msg.method == kHttpHead));
|
||||
DestroyHttpMessage(&msg);
|
||||
close(client);
|
||||
--connections;
|
||||
}
|
||||
--listening;
|
||||
|
||||
// inform the parent that this clone has finished
|
||||
CloseWorker:
|
||||
close(server);
|
||||
kprintf(" #%.2ld", (intptr_t)id);
|
||||
__atomic_sub_fetch(&workers, 1, __ATOMIC_SEQ_CST);
|
||||
WorkerFinished:
|
||||
--workers;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OnCtrlC(int sig) {
|
||||
closingtime = true;
|
||||
status = " shutting down...";
|
||||
}
|
||||
|
||||
void PrintStatus(void) {
|
||||
kprintf("\r\e[K\e[32mgreenbean\e[0m "
|
||||
"workers=%d "
|
||||
"listening=%d "
|
||||
"connections=%d "
|
||||
"messages=%d%s ",
|
||||
workers, listening, connections, messages, status);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int64_t loadtzdbearly;
|
||||
int i, gotsome, haveleft, ready = 1;
|
||||
int i;
|
||||
char **tls;
|
||||
char **stack;
|
||||
uint32_t *hostips;
|
||||
// ShowCrashReports();
|
||||
|
||||
ShowCrashReports();
|
||||
kprintf("welcome to greenbean\n");
|
||||
gmtime(&loadtzdbearly);
|
||||
// listen for ctrl-c, hangup, and kill which shut down greenbean
|
||||
status = "";
|
||||
struct sigaction sa = {.sa_handler = OnCtrlC};
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
|
||||
// spawn a bunch of threads
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
void *stack = mmap(0, 65536, PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||
clone(Worker, stack, 65536,
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
||||
(void *)(intptr_t)i, 0, 0, 0, 0);
|
||||
// print all the ips that 0.0.0.0 will bind
|
||||
for (hostips = GetHostIps(), i = 0; hostips[i]; ++i) {
|
||||
kprintf("listening on http://%d.%d.%d.%d:%d\n",
|
||||
(hostips[i] & 0xff000000) >> 030, (hostips[i] & 0x00ff0000) >> 020,
|
||||
(hostips[i] & 0x0000ff00) >> 010, (hostips[i] & 0x000000ff) >> 000,
|
||||
PORT);
|
||||
}
|
||||
|
||||
// wait for all threads to spawn
|
||||
for (;;) {
|
||||
__atomic_load(&workers, &gotsome, __ATOMIC_SEQ_CST);
|
||||
if (workers == THREADS) break;
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
|
||||
// all threads are spawned so unleash the barrier
|
||||
kprintf("\ngreenbean is ready to go\n");
|
||||
sigaction(SIGINT, &(struct sigaction){.sa_handler = OnCtrlC}, 0);
|
||||
__atomic_store(&barrier, &ready, __ATOMIC_SEQ_CST);
|
||||
|
||||
// main process does nothing until it's closing time
|
||||
for (;;) {
|
||||
__atomic_load(&workers, &haveleft, __ATOMIC_SEQ_CST);
|
||||
if (!haveleft) break;
|
||||
__builtin_ia32_pause();
|
||||
if (usleep(HEARTBEAT * 1000) == -1 && closingtime) {
|
||||
kprintf("\rgreenbean is shutting down...\n");
|
||||
// spawn over 9,000 worker threads
|
||||
tls = 0;
|
||||
stack = 0;
|
||||
threads = argc > 1 ? atoi(argv[1]) : GetCpuCount();
|
||||
if ((1 <= threads && threads <= INT_MAX) &&
|
||||
(tls = malloc(threads * sizeof(*tls))) &&
|
||||
(stack = malloc(threads * sizeof(*stack)))) {
|
||||
for (i = 0; i < threads; ++i) {
|
||||
if ((tls[i] = __initialize_tls(malloc(64))) &&
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) {
|
||||
++workers;
|
||||
if (clone(Worker, stack[i], GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||
CLONE_SIGHAND | CLONE_SETTLS | CLONE_CHILD_SETTID |
|
||||
CLONE_CHILD_CLEARTID,
|
||||
(void *)(intptr_t)i, 0, tls[i], 64,
|
||||
(int *)(tls[i] + 0x38)) == -1) {
|
||||
--workers;
|
||||
kprintf("error: clone(%d) failed %m\n", i);
|
||||
}
|
||||
} else {
|
||||
kprintf("error: mmap(%d) failed %m\n", i);
|
||||
}
|
||||
if (!(i % 500)) {
|
||||
PrintStatus();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
kprintf("error: invalid number of threads\n");
|
||||
}
|
||||
|
||||
kprintf("\n");
|
||||
kprintf("thank you for flying greenbean\n");
|
||||
// wait for workers to terminate
|
||||
while (workers) {
|
||||
PrintStatus();
|
||||
usleep(HEARTBEAT * 1000);
|
||||
}
|
||||
|
||||
// clean up terminal line
|
||||
kprintf("\r\e[K");
|
||||
|
||||
// clean up memory
|
||||
for (i = 0; i < threads; ++i) {
|
||||
if (stack) munmap(stack[i], GetStackSize());
|
||||
if (tls) free(tls[i]);
|
||||
}
|
||||
free(hostips);
|
||||
free(stack);
|
||||
free(tls);
|
||||
}
|
||||
|
|
|
@ -8,22 +8,10 @@
|
|||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Command for updating timestamps on files.
|
||||
*/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (touch(argv[i], 0666) == -1) {
|
||||
fprintf(stderr, "ERROR: %s: %s\n", argv[i], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
creat("hello.txt", 0644);
|
||||
write(3, "hello\n", 6);
|
||||
close(3);
|
||||
return 0;
|
||||
}
|
|
@ -7,23 +7,30 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/dce.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Non-Self-Modifying APE Binary Demo
|
||||
*
|
||||
* See examples/examples.mk for the build config, which uses the
|
||||
* alternative APE runtime.
|
||||
*/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, j;
|
||||
bool wantnewline;
|
||||
if (argc > 1 && !strcmp(argv[1], "-n")) {
|
||||
i = 2;
|
||||
wantnewline = false;
|
||||
if (_base[0] == 'M' && _base[1] == 'Z') {
|
||||
printf("success: %s spawned without needing to modify its "
|
||||
"executable header",
|
||||
argv[0]);
|
||||
if (!IsWindows()) {
|
||||
printf(", thanks to APE loader!\n");
|
||||
} else {
|
||||
printf(", because you ran it on Windows :P\n");
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
i = 1;
|
||||
wantnewline = true;
|
||||
printf("error: %s doesn't have an MZ file header!\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
for (j = 0; i + j < argc; ++j) {
|
||||
if (j) fputc(' ', stdout);
|
||||
fputs(argv[i + j], stdout);
|
||||
}
|
||||
if (wantnewline) fputc('\n', stdout);
|
||||
return 0;
|
||||
}
|
|
@ -60,7 +60,7 @@ o/$(MODE)/examples/package/%.com.dbg: \
|
|||
$(EXAMPLES_PACKAGE_DEPS) \
|
||||
o/$(MODE)/examples/package/%.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
# Invalidates objects in package when makefile is edited.
|
||||
|
|
|
@ -95,7 +95,7 @@ o/$(MODE)/examples/pyapp/pyapp.com.dbg: \
|
|||
o/$(MODE)/examples/pyapp/pyapp.pkg \
|
||||
o/$(MODE)/examples/pyapp/pyapp.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
$(LINK) $(LINKARGS) -o $@
|
||||
|
||||
# # Unwrap the APE .COM binary, that's embedded within the linked file
|
||||
|
|
80
libc/atomic.h
Normal file
80
libc/atomic.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ATOMIC_H_
|
||||
#define COSMOPOLITAN_LIBC_ATOMIC_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/**
|
||||
* @fileoverview C11 Atomic Types
|
||||
*
|
||||
* We supoprt C++ and old C compilers. It's recommended you use macros
|
||||
* like `_Atomic(int)` rather than `_Atomic int` or `atomic_int` since
|
||||
* we only define a portability macro for the syntax `_Atomic(T)`.
|
||||
*
|
||||
* @see libc/integral/c.inc
|
||||
* @see libc/bits/atomic.h
|
||||
*/
|
||||
|
||||
#define atomic_bool _Atomic(_Bool)
|
||||
#define atomic_bool32 atomic_int32
|
||||
#define atomic_char _Atomic(char)
|
||||
#define atomic_schar _Atomic(signed char)
|
||||
#define atomic_uchar _Atomic(unsigned char)
|
||||
#define atomic_short _Atomic(short)
|
||||
#define atomic_ushort _Atomic(unsigned short)
|
||||
#define atomic_int _Atomic(int)
|
||||
#define atomic_uint _Atomic(unsigned int)
|
||||
#define atomic_long _Atomic(long)
|
||||
#define atomic_ulong _Atomic(unsigned long)
|
||||
#define atomic_llong _Atomic(long long)
|
||||
#define atomic_ullong _Atomic(unsigned long long)
|
||||
#define atomic_char16_t _Atomic(char16_t)
|
||||
#define atomic_char32_t _Atomic(char32_t)
|
||||
#define atomic_wchar_t _Atomic(wchar_t)
|
||||
#define atomic_intptr_t _Atomic(intptr_t)
|
||||
#define atomic_uintptr_t _Atomic(uintptr_t)
|
||||
#define atomic_size_t _Atomic(size_t)
|
||||
#define atomic_ptrdiff_t _Atomic(ptrdiff_t)
|
||||
#define atomic_int_fast8_t _Atomic(int_fast8_t)
|
||||
#define atomic_uint_fast8_t _Atomic(uint_fast8_t)
|
||||
#define atomic_int_fast16_t _Atomic(int_fast16_t)
|
||||
#define atomic_uint_fast16_t _Atomic(uint_fast16_t)
|
||||
#define atomic_int_fast32_t _Atomic(int_fast32_t)
|
||||
#define atomic_uint_fast32_t _Atomic(uint_fast32_t)
|
||||
#define atomic_int_fast64_t _Atomic(int_fast64_t)
|
||||
#define atomic_uint_fast64_t _Atomic(uint_fast64_t)
|
||||
#define atomic_int_least8_t _Atomic(int_least8_t)
|
||||
#define atomic_uint_least8_t _Atomic(uint_least8_t)
|
||||
#define atomic_int_least16_t _Atomic(int_least16_t)
|
||||
#define atomic_uint_least16_t _Atomic(uint_least16_t)
|
||||
#define atomic_int_least32_t _Atomic(int_least32_t)
|
||||
#define atomic_uint_least32_t _Atomic(uint_least32_t)
|
||||
#define atomic_int_least64_t _Atomic(int_least64_t)
|
||||
#define atomic_uint_least64_t _Atomic(uint_least64_t)
|
||||
|
||||
#ifdef __CLANG_ATOMIC_BOOL_LOCK_FREE
|
||||
#define ATOMIC_BOOL_LOCK_FREE __CLANG_ATOMIC_BOOL_LOCK_FREE
|
||||
#define ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE
|
||||
#define ATOMIC_CHAR16_T_LOCK_FREE __CLANG_ATOMIC_CHAR16_T_LOCK_FREE
|
||||
#define ATOMIC_CHAR32_T_LOCK_FREE __CLANG_ATOMIC_CHAR32_T_LOCK_FREE
|
||||
#define ATOMIC_WCHAR_T_LOCK_FREE __CLANG_ATOMIC_WCHAR_T_LOCK_FREE
|
||||
#define ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE
|
||||
#define ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE
|
||||
#define ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE
|
||||
#define ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE
|
||||
#define ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE
|
||||
#else
|
||||
#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE
|
||||
#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
|
||||
#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE
|
||||
#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE
|
||||
#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE
|
||||
#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
|
||||
#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
|
||||
#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
|
||||
#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
|
||||
#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ATOMIC_H_ */
|
|
@ -1,18 +1,18 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_H_
|
||||
#define COSMOPOLITAN_LIBC_BITS_ATOMIC_H_
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/atomic.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/**
|
||||
* @fileoverview C11 version of The Cosmopolitan Atomics Library.
|
||||
* @fileoverview Cosmopolitan C11 Atomics Library
|
||||
*
|
||||
* - Forty-two different ways to say MOV.
|
||||
* - Fourteen different ways to say XCHG.
|
||||
* - Twenty different ways to say LOCK CMPXCHG.
|
||||
*
|
||||
* Living proof high-level languages can be lower-level than assembly.
|
||||
* It's a lower level programming language than assembly!
|
||||
*
|
||||
* @see libc/atomic.h
|
||||
*/
|
||||
|
||||
#define memory_order int
|
||||
|
@ -23,31 +23,148 @@ COSMOPOLITAN_C_START_
|
|||
#define memory_order_acq_rel 4
|
||||
#define memory_order_seq_cst 5
|
||||
|
||||
#define atomic_flag struct AtomicFlag
|
||||
#define atomic_flag_clear(PTR) atomic_store((PTR)->__cacheline, 0)
|
||||
#define atomic_flag_test_and_set(PTR) \
|
||||
({ \
|
||||
uint32_t ax = 0; \
|
||||
lockcmpxchg((PTR)->__cacheline, &ax, 1); \
|
||||
#define ATOMIC_VAR_INIT(value) (value)
|
||||
#define atomic_is_lock_free(obj) ((void)(obj), sizeof(obj) <= sizeof(void *))
|
||||
|
||||
#define atomic_flag atomic_bool
|
||||
#define ATOMIC_FLAG_INIT ATOMIC_VAR_INIT(0)
|
||||
#define atomic_flag_test_and_set_explicit(x, order) \
|
||||
atomic_exchange_explicit(x, 1, order)
|
||||
#define atomic_flag_clear_explicit(x, order) atomic_store_explicit(x, 0, order)
|
||||
|
||||
#define atomic_compare_exchange_strong(pObject, pExpected, desired) \
|
||||
atomic_compare_exchange_strong_explicit( \
|
||||
pObject, pExpected, desired, memory_order_seq_cst, memory_order_seq_cst)
|
||||
#define atomic_compare_exchange_weak(pObject, pExpected, desired) \
|
||||
atomic_compare_exchange_weak_explicit( \
|
||||
pObject, pExpected, desired, memory_order_seq_cst, memory_order_seq_cst)
|
||||
#define atomic_exchange(pObject, desired) \
|
||||
atomic_exchange_explicit(pObject, desired, memory_order_seq_cst)
|
||||
#define atomic_fetch_add(pObject, operand) \
|
||||
atomic_fetch_add_explicit(pObject, operand, memory_order_seq_cst)
|
||||
#define atomic_fetch_and(pObject, operand) \
|
||||
atomic_fetch_and_explicit(pObject, operand, memory_order_seq_cst)
|
||||
#define atomic_fetch_or(pObject, operand) \
|
||||
atomic_fetch_or_explicit(pObject, operand, memory_order_seq_cst)
|
||||
#define atomic_fetch_sub(pObject, operand) \
|
||||
atomic_fetch_sub_explicit(pObject, operand, memory_order_seq_cst)
|
||||
#define atomic_fetch_xor(pObject, operand) \
|
||||
atomic_fetch_xor_explicit(pObject, operand, memory_order_seq_cst)
|
||||
#define atomic_load(pObject) atomic_load_explicit(pObject, memory_order_seq_cst)
|
||||
#define atomic_store(pObject, desired) \
|
||||
atomic_store_explicit(pObject, desired, memory_order_seq_cst)
|
||||
#define atomic_flag_test_and_set(x) \
|
||||
atomic_flag_test_and_set_explicit(x, memory_order_seq_cst)
|
||||
#define atomic_flag_clear(x) atomic_flag_clear_explicit(x, memory_order_seq_cst)
|
||||
|
||||
#if defined(__CLANG_ATOMIC_BOOL_LOCK_FREE)
|
||||
|
||||
#define atomic_init(obj, value) __c11_atomic_init(obj, value)
|
||||
#define atomic_thread_fence(order) __c11_atomic_thread_fence(order)
|
||||
#define atomic_signal_fence(order) __c11_atomic_signal_fence(order)
|
||||
#define atomic_compare_exchange_strong_explicit(object, expected, desired, \
|
||||
success, failure) \
|
||||
__c11_atomic_compare_exchange_strong(object, expected, desired, success, \
|
||||
failure)
|
||||
#define atomic_compare_exchange_weak_explicit(object, expected, desired, \
|
||||
success, failure) \
|
||||
__c11_atomic_compare_exchange_weak(object, expected, desired, success, \
|
||||
failure)
|
||||
#define atomic_exchange_explicit(object, desired, order) \
|
||||
__c11_atomic_exchange(object, desired, order)
|
||||
#define atomic_fetch_add_explicit(object, operand, order) \
|
||||
__c11_atomic_fetch_add(object, operand, order)
|
||||
#define atomic_fetch_and_explicit(object, operand, order) \
|
||||
__c11_atomic_fetch_and(object, operand, order)
|
||||
#define atomic_fetch_or_explicit(object, operand, order) \
|
||||
__c11_atomic_fetch_or(object, operand, order)
|
||||
#define atomic_fetch_sub_explicit(object, operand, order) \
|
||||
__c11_atomic_fetch_sub(object, operand, order)
|
||||
#define atomic_fetch_xor_explicit(object, operand, order) \
|
||||
__c11_atomic_fetch_xor(object, operand, order)
|
||||
#define atomic_load_explicit(object, order) __c11_atomic_load(object, order)
|
||||
#define atomic_store_explicit(object, desired, order) \
|
||||
__c11_atomic_store(object, desired, order)
|
||||
|
||||
#elif (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407
|
||||
|
||||
#define atomic_init(obj, value) ((void)(*(obj) = (value)))
|
||||
#define atomic_thread_fence(order) __atomic_thread_fence(order)
|
||||
#define atomic_signal_fence(order) __atomic_signal_fence(order)
|
||||
#define atomic_compare_exchange_strong_explicit(pObject, pExpected, desired, \
|
||||
success, failure) \
|
||||
__atomic_compare_exchange_n(pObject, pExpected, desired, 0, success, failure)
|
||||
#define atomic_compare_exchange_weak_explicit(pObject, pExpected, desired, \
|
||||
success, failure) \
|
||||
__atomic_compare_exchange_n(pObject, pExpected, desired, 1, success, failure)
|
||||
#define atomic_exchange_explicit(pObject, desired, order) \
|
||||
__atomic_exchange_n(pObject, desired, order)
|
||||
#define atomic_fetch_add_explicit(pObject, operand, order) \
|
||||
__atomic_fetch_add(pObject, operand, order)
|
||||
#define atomic_fetch_and_explicit(pObject, operand, order) \
|
||||
__atomic_fetch_and(pObject, operand, order)
|
||||
#define atomic_fetch_or_explicit(pObject, operand, order) \
|
||||
__atomic_fetch_or(pObject, operand, order)
|
||||
#define atomic_fetch_sub_explicit(pObject, operand, order) \
|
||||
__atomic_fetch_sub(pObject, operand, order)
|
||||
#define atomic_fetch_xor_explicit(pObject, operand, order) \
|
||||
__atomic_fetch_xor(pObject, operand, order)
|
||||
#define atomic_load_explicit(pObject, order) __atomic_load_n(pObject, order)
|
||||
#define atomic_store_explicit(pObject, desired, order) \
|
||||
__atomic_store_n(pObject, desired, order)
|
||||
|
||||
#else
|
||||
|
||||
#define atomic_init(obj, value) ((void)(*(obj) = (value)))
|
||||
#define atomic_thread_fence(order) __sync_synchronize()
|
||||
#define atomic_signal_fence(order) __asm__ volatile("" ::: "memory")
|
||||
#define __atomic_apply_stride(object, operand) \
|
||||
(((__typeof__(__atomic_val(object)))0) + (operand))
|
||||
#define atomic_compare_exchange_strong_explicit(object, expected, desired, \
|
||||
success, failure) \
|
||||
__extension__({ \
|
||||
__typeof__(expected) __ep = (expected); \
|
||||
__typeof__(*__ep) __e = *__ep; \
|
||||
(void)(success); \
|
||||
(void)(failure); \
|
||||
(_Bool)((*__ep = __sync_val_compare_and_swap(object, __e, desired)) == \
|
||||
__e); \
|
||||
})
|
||||
#define atomic_init(PTR, VAL) atomic_store(PTR, VAL)
|
||||
#define atomic_exchange(PTR, VAL) lockxchg(PTR, &(VAL))
|
||||
#define atomic_compare_exchange_strong(X, Y, Z) _lockcmpxchg(X, Y, Z)
|
||||
#define atomic_compare_exchange_weak(X, Y, Z) _lockcmpxchg(X, Y, Z)
|
||||
#define atomic_load_explicit(PTR, ORDER) atomic_load(PTR)
|
||||
#define atomic_store_explicit(PTR, VAL, ORDER) atomic_store(PTR, VAL)
|
||||
#define atomic_flag_clear_explicit(PTR, ORDER) atomic_store(PTR, 0)
|
||||
#define atomic_exchange_explicit(PTR, VAL, ORDER) lockxchg(PTR, &(VAL))
|
||||
#define atomic_flag_test_and_set_explicit(PTR, ORDER) lockcmpxchg(PTR, 0, 1)
|
||||
#define atomic_compare_exchange_strong_explicit(X, Y, Z, S, F) \
|
||||
lockcmpxchg(X, Y, Z)
|
||||
#define atomic_compare_exchange_weak_explicit(X, Y, Z, S, F) \
|
||||
lockcmpxchg(X, Y, Z)
|
||||
#define atomic_compare_exchange_weak_explicit(object, expected, desired, \
|
||||
success, failure) \
|
||||
atomic_compare_exchange_strong_explicit(object, expected, desired, success, \
|
||||
failure)
|
||||
#if __has_builtin(__sync_swap)
|
||||
#define atomic_exchange_explicit(object, desired, order) \
|
||||
((void)(order), __sync_swap(object, desired))
|
||||
#else
|
||||
#define atomic_exchange_explicit(object, desired, order) \
|
||||
__extension__({ \
|
||||
__typeof__(object) __o = (object); \
|
||||
__typeof__(desired) __d = (desired); \
|
||||
(void)(order); \
|
||||
__sync_synchronize(); \
|
||||
__sync_lock_test_and_set(&__atomic_val(__o), __d); \
|
||||
})
|
||||
#endif
|
||||
#define atomic_fetch_add_explicit(object, operand, order) \
|
||||
((void)(order), \
|
||||
__sync_fetch_and_add(object, __atomic_apply_stride(object, operand)))
|
||||
#define atomic_fetch_and_explicit(object, operand, order) \
|
||||
((void)(order), __sync_fetch_and_and(object, operand))
|
||||
#define atomic_fetch_or_explicit(object, operand, order) \
|
||||
((void)(order), __sync_fetch_and_or(object, operand))
|
||||
#define atomic_fetch_sub_explicit(object, operand, order) \
|
||||
((void)(order), \
|
||||
__sync_fetch_and_sub(object, __atomic_apply_stride(object, operand)))
|
||||
#define atomic_fetch_xor_explicit(object, operand, order) \
|
||||
((void)(order), __sync_fetch_and_xor(object, operand))
|
||||
#define atomic_load_explicit(object, order) \
|
||||
((void)(order), __sync_fetch_and_add(object, 0))
|
||||
#define atomic_store_explicit(object, desired, order) \
|
||||
((void)atomic_exchange_explicit(object, desired, order))
|
||||
|
||||
struct AtomicFlag {
|
||||
uint32_t __cacheline[16]; /* Intel V.O §9.4.6 */
|
||||
} forcealign(64);
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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 │
|
||||
|
@ -18,12 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
// Blocks until data from stream buffer is written out.
|
||||
//
|
||||
// @param rdi is the stream handle
|
||||
// @return 0 on success or -1 w/ errno
|
||||
// @see fflush_unlocked()
|
||||
fflush: mov %rdi,%r11
|
||||
ezlea fflush_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fflush,globl
|
||||
.initbss 201,_init___clock_gettime
|
||||
__clock_gettime:
|
||||
.quad 0
|
||||
.endobj __clock_gettime,globl,hidden
|
||||
.previous
|
||||
|
||||
.init.start 201,_init___clock_gettime
|
||||
ezlea __clock_gettime_init,ax
|
||||
stosq
|
||||
.init.end 201,_init___clock_gettime
|
19
libc/calls/asan.internal.h
Normal file
19
libc/calls/asan.internal.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_
|
||||
#include "libc/bits/asmflag.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
forceinline bool __asan_is_valid_timespec(const struct timespec *ts) {
|
||||
bool zf;
|
||||
asm(ZFLAG_ASM("cmpw\t$0,0x7fff8000(%1)")
|
||||
: ZFLAG_CONSTRAINT(zf)
|
||||
: "r"((intptr_t)ts >> 3)
|
||||
: "memory");
|
||||
return zf;
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_ */
|
|
@ -130,18 +130,18 @@ int geteuid(void) nosideeffect;
|
|||
int getgid(void) nosideeffect;
|
||||
int gethostname(char *, size_t);
|
||||
int getloadavg(double *, int);
|
||||
int getpgid(int);
|
||||
int getpgid(int) nosideeffect libcesque;
|
||||
int getpgrp(void) nosideeffect;
|
||||
int getpid(void);
|
||||
int getpid(void) nosideeffect libcesque;
|
||||
int getppid(void);
|
||||
int getpriority(int, unsigned);
|
||||
int getresgid(uint32_t *, uint32_t *, uint32_t *);
|
||||
int getresuid(uint32_t *, uint32_t *, uint32_t *);
|
||||
int getrlimit(int, struct rlimit *);
|
||||
int getrusage(int, struct rusage *);
|
||||
int getsid(int) nosideeffect;
|
||||
int gettid(void);
|
||||
int getuid(void) nosideeffect;
|
||||
int getsid(int) nosideeffect libcesque;
|
||||
int gettid(void) libcesque;
|
||||
int getuid(void) nosideeffect libcesque;
|
||||
int kill(int, int);
|
||||
int killpg(int, int);
|
||||
int link(const char *, const char *) dontthrow;
|
||||
|
|
|
@ -65,20 +65,39 @@ $(LIBC_CALLS_A).pkg: \
|
|||
$(LIBC_CALLS_A_OBJS) \
|
||||
$(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/libc/calls/vdsofunc.greg.o \
|
||||
o/$(MODE)/libc/calls/directmap.o \
|
||||
o/$(MODE)/libc/calls/directmap-nt.o \
|
||||
o/$(MODE)/libc/calls/mapstack.greg.o \
|
||||
o/$(MODE)/libc/calls/getcwd.greg.o \
|
||||
o/$(MODE)/libc/calls/getcwd-xnu.greg.o \
|
||||
o/$(MODE)/libc/calls/getprogramexecutablename.greg.o \
|
||||
o/$(MODE)/libc/calls/raise.o: \
|
||||
# we can't use asan because:
|
||||
# ucontext_t memory is owned by xnu kernel
|
||||
o/$(MODE)/libc/calls/sigenter-xnu.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffreestanding \
|
||||
$(NO_MAGIC)
|
||||
-fno-sanitize=address
|
||||
|
||||
o/$(MODE)/libc/calls/termios2linux.o \
|
||||
o/$(MODE)/libc/calls/termios2host.o \
|
||||
# we can't use asan because:
|
||||
# vdso memory is owned by linux kernel
|
||||
o/$(MODE)/libc/calls/vdsofunc.greg.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
|
||||
# we can't use asan because:
|
||||
# asan guard pages haven't been allocated yet
|
||||
o/$(MODE)/libc/calls/directmap.o \
|
||||
o/$(MODE)/libc/calls/directmap-nt.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
|
||||
# we can't use asan because:
|
||||
# ntspawn allocates 128kb of heap memory via win32
|
||||
o/$(MODE)/libc/calls/ntspawn.o \
|
||||
o/$(MODE)/libc/calls/mkntcmdline.o \
|
||||
o/$(MODE)/libc/calls/mkntenvblock.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
|
||||
# we always want -O3 because:
|
||||
# it makes the code size smaller too
|
||||
o/$(MODE)/libc/calls/sigenter-freebsd.o \
|
||||
o/$(MODE)/libc/calls/sigenter-netbsd.o \
|
||||
o/$(MODE)/libc/calls/sigenter-openbsd.o \
|
||||
|
@ -87,7 +106,43 @@ o/$(MODE)/libc/calls/ntcontext2linux.o: \
|
|||
OVERRIDE_COPTS += \
|
||||
-O3
|
||||
|
||||
# TODO(jart): make va_arg optimize well in default mode
|
||||
# we must disable static stack safety because:
|
||||
# these functions use alloca(n)
|
||||
o/$(MODE)/libc/calls/execl.o \
|
||||
o/$(MODE)/libc/calls/execle.o \
|
||||
o/$(MODE)/libc/calls/execlp.o \
|
||||
o/$(MODE)/libc/calls/execve-sysv.o \
|
||||
o/$(MODE)/libc/calls/execve-nt.greg.o \
|
||||
o/$(MODE)/libc/calls/mkntenvblock.o: \
|
||||
OVERRIDE_CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
# we must disable static stack safety because:
|
||||
# PATH_MAX*sizeof(char16_t)*2 exceeds 4096 byte frame limit
|
||||
o/$(MODE)/libc/calls/copyfile.o \
|
||||
o/$(MODE)/libc/calls/symlinkat-nt.o \
|
||||
o/$(MODE)/libc/calls/readlinkat-nt.o \
|
||||
o/$(MODE)/libc/calls/linkat-nt.o \
|
||||
o/$(MODE)/libc/calls/renameat-nt.o: \
|
||||
OVERRIDE_CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
# we must segregate codegen because:
|
||||
# file contains multiple independently linkable apis
|
||||
o/$(MODE)/libc/calls/ioctl-siocgifconf.o \
|
||||
o/$(MODE)/libc/calls/ioctl-siocgifconf-nt.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
|
||||
# we want small code size because:
|
||||
# to keep .text.head under 4096 bytes
|
||||
o/$(MODE)/libc/calls/mman.greg.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-Os
|
||||
|
||||
# we always want -Os because:
|
||||
# va_arg codegen is very bloated in default mode
|
||||
o//libc/calls/open.o \
|
||||
o//libc/calls/openat.o \
|
||||
o//libc/calls/prctl.o \
|
||||
|
@ -109,27 +164,6 @@ o//libc/calls/fcntl.o: \
|
|||
OVERRIDE_CFLAGS += \
|
||||
-Os
|
||||
|
||||
# must use alloca() or path_max*2*2
|
||||
o/$(MODE)/libc/calls/execl.o \
|
||||
o/$(MODE)/libc/calls/execle.o \
|
||||
o/$(MODE)/libc/calls/execlp.o \
|
||||
o/$(MODE)/libc/calls/copyfile.o \
|
||||
o/$(MODE)/libc/calls/execve-nt.o \
|
||||
o/$(MODE)/libc/calls/linkat-nt.o \
|
||||
o/$(MODE)/libc/calls/renameat-nt.o \
|
||||
o/$(MODE)/libc/calls/execve-sysv.o \
|
||||
o/$(MODE)/libc/calls/symlinkat-nt.o \
|
||||
o/$(MODE)/libc/calls/readlinkat-nt.o \
|
||||
o/$(MODE)/libc/calls/mkntenvblock.o: \
|
||||
OVERRIDE_CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
o/$(MODE)/libc/calls/ioctl-siocgifconf.o \
|
||||
o/$(MODE)/libc/calls/ioctl-siocgifconf-nt.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
|
||||
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
|
||||
LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_CALLS_HDRS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_HDRS))
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/errors.h"
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
||||
/**
|
||||
* Changes root directory.
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
|
@ -28,6 +27,7 @@ textwindows int sys_clock_gettime_nt(int clockid, struct timespec *ts) {
|
|||
struct timespec res;
|
||||
struct NtFileTime ft;
|
||||
static struct timespec mono;
|
||||
if (!ts) return efault();
|
||||
if (clockid == CLOCK_REALTIME) {
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
*ts = FileTimeToTimeSpec(ft);
|
||||
|
|
35
libc/calls/clock_gettime-xnu.c
Normal file
35
libc/calls/clock_gettime-xnu.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*-*- 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 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/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
|
||||
int sys_clock_gettime_xnu(int clockid, struct timespec *ts) {
|
||||
axdx_t ad;
|
||||
ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL);
|
||||
if (ad.ax != -1) {
|
||||
if (ad.ax) {
|
||||
ts->tv_sec = ad.ax;
|
||||
ts->tv_nsec = ad.dx;
|
||||
}
|
||||
ts->tv_nsec *= 1000;
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -17,18 +17,23 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/asmflag.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/asan.internal.h"
|
||||
#include "libc/calls/clock_gettime.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime;
|
||||
|
||||
/**
|
||||
* Returns nanosecond time.
|
||||
*
|
||||
|
@ -36,6 +41,13 @@ static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime;
|
|||
* time. Among the more popular is CLOCK_MONOTONIC. This function has a
|
||||
* zero syscall implementation of that on modern x86.
|
||||
*
|
||||
* nowl l: 45𝑐 15𝑛𝑠
|
||||
* rdtsc l: 13𝑐 4𝑛𝑠
|
||||
* gettimeofday l: 44𝑐 14𝑛𝑠
|
||||
* clock_gettime l: 40𝑐 13𝑛𝑠
|
||||
* __clock_gettime l: 35𝑐 11𝑛𝑠
|
||||
* sys_clock_gettime l: 220𝑐 71𝑛𝑠
|
||||
*
|
||||
* @param clockid can be CLOCK_REALTIME, CLOCK_MONOTONIC, etc.
|
||||
* @param ts is where the result is stored
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
|
@ -44,54 +56,49 @@ static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime;
|
|||
* @asyncsignalsafe
|
||||
*/
|
||||
noinstrument int clock_gettime(int clockid, struct timespec *ts) {
|
||||
int rc, e;
|
||||
axdx_t ad;
|
||||
char buf[45];
|
||||
if (!ts) {
|
||||
int rc;
|
||||
char *buf;
|
||||
if (IsAsan() && !__asan_is_valid_timespec(ts)) {
|
||||
rc = efault();
|
||||
} else if (IsAsan() && !__asan_is_valid(ts, sizeof(*ts))) {
|
||||
rc = efault();
|
||||
} else if (clockid == -1) {
|
||||
rc = einval();
|
||||
} else if (!IsWindows()) {
|
||||
e = errno;
|
||||
if ((rc = __clock_gettime(clockid, ts))) {
|
||||
errno = e;
|
||||
ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL);
|
||||
assert(ad.ax != -1);
|
||||
if (SupportsXnu() && ad.ax) {
|
||||
ts->tv_sec = ad.ax;
|
||||
ts->tv_nsec = ad.dx;
|
||||
}
|
||||
ts->tv_nsec *= 1000;
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
rc = sys_clock_gettime_nt(clockid, ts);
|
||||
rc = __clock_gettime(clockid, ts);
|
||||
}
|
||||
#if SYSDEBUG
|
||||
if (!__time_critical) {
|
||||
buf = alloca(45);
|
||||
STRACE("clock_gettime(%d, [%s]) → %d% m", clockid,
|
||||
DescribeTimespec(buf, sizeof(buf), rc, ts), rc);
|
||||
DescribeTimespec(buf, 45, rc, ts), rc);
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fast system clock_gettime() if it exists.
|
||||
* Returns pointer to fastest clock_gettime().
|
||||
*/
|
||||
void *__get_clock_gettime(void) {
|
||||
void *vdso;
|
||||
static bool once;
|
||||
static void *result;
|
||||
if (!once) {
|
||||
if ((vdso = __vdsofunc("__vdso_clock_gettime"))) {
|
||||
__clock_gettime = result = vdso;
|
||||
}
|
||||
once = true;
|
||||
clock_gettime_f *__clock_gettime_get(bool *opt_out_isfast) {
|
||||
bool isfast;
|
||||
clock_gettime_f *res;
|
||||
if (IsLinux() && (res = __vdsosym("LINUX_2.6", "__vdso_clock_gettime"))) {
|
||||
isfast = true;
|
||||
} else if (IsXnu()) {
|
||||
isfast = false;
|
||||
res = sys_clock_gettime_xnu;
|
||||
} else if (IsWindows()) {
|
||||
isfast = true;
|
||||
res = sys_clock_gettime_nt;
|
||||
} else {
|
||||
isfast = false;
|
||||
res = sys_clock_gettime;
|
||||
}
|
||||
return result;
|
||||
if (opt_out_isfast) {
|
||||
*opt_out_isfast = isfast;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const void *const __clock_gettime_ctor[] initarray = {
|
||||
__get_clock_gettime,
|
||||
};
|
||||
hidden int __clock_gettime_init(int clockid, struct timespec *ts) {
|
||||
clock_gettime_f *gettime;
|
||||
__clock_gettime = gettime = __clock_gettime_get(0);
|
||||
return gettime(clockid, ts);
|
||||
}
|
||||
|
|
15
libc/calls/clock_gettime.h
Normal file
15
libc/calls/clock_gettime.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
typedef int clock_gettime_f(int, struct timespec *);
|
||||
|
||||
extern clock_gettime_f *__clock_gettime;
|
||||
hidden clock_gettime_f __clock_gettime_init;
|
||||
hidden clock_gettime_f *__clock_gettime_get(bool *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_ */
|
|
@ -21,7 +21,6 @@
|
|||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sock/ntstdin.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
@ -42,12 +41,7 @@ textwindows int sys_close_nt(struct Fd *fd) {
|
|||
// if this file descriptor is wrapped in a named pipe worker thread
|
||||
// then we need to close our copy of the worker thread handle. it's
|
||||
// also required that whatever install a worker use malloc, so free
|
||||
if (fd->worker) {
|
||||
if (!weaken(UnrefNtStdinWorker)(fd->worker)) ok = false;
|
||||
fd->worker = 0;
|
||||
} else {
|
||||
if (!CloseHandle(fd->handle)) ok = false;
|
||||
}
|
||||
if (!CloseHandle(fd->handle)) ok = false;
|
||||
if (fd->kind == kFdConsole && fd->extra && fd->extra != -1) {
|
||||
if (!CloseHandle(fd->extra)) ok = false;
|
||||
}
|
||||
|
|
|
@ -17,11 +17,13 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
|
@ -46,6 +48,7 @@
|
|||
*/
|
||||
int close(int fd) {
|
||||
int rc;
|
||||
_spinlock(&__fds_lock);
|
||||
if (fd == -1) {
|
||||
rc = 0;
|
||||
} else if (fd < 0) {
|
||||
|
@ -74,9 +77,10 @@ int close(int fd) {
|
|||
}
|
||||
}
|
||||
if (!__vforked) {
|
||||
__releasefd(fd);
|
||||
__releasefd_unlocked(fd);
|
||||
}
|
||||
}
|
||||
_spunlock(&__fds_lock);
|
||||
STRACE("%s(%d) → %d% m", "close", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/copyfile.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
|
|
|
@ -16,24 +16,22 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/nexgen32e/rdtscp.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/calls/struct/sigaltstack.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
||||
TEST(nowl, testIsMonotonic) {
|
||||
long double a = nowl();
|
||||
long double b = nowl();
|
||||
EXPECT_TRUE(b > a);
|
||||
}
|
||||
|
||||
BENCH(nowl, bench) {
|
||||
volatile int64_t c;
|
||||
volatile long double x;
|
||||
volatile struct timespec ts;
|
||||
EZBENCH2("rdtsc", donothing, c = rdtsc());
|
||||
EZBENCH2("nowl", donothing, x = nowl());
|
||||
EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_MONOTONIC, &ts));
|
||||
const char *DescribeSigaltstk(char *buf, size_t bufsize, int rc,
|
||||
const struct sigaltstack *ss) {
|
||||
if (rc == -1) return "n/a";
|
||||
if (!ss) return "NULL";
|
||||
if ((!IsAsan() && kisdangerous(ss)) ||
|
||||
(IsAsan() && !__asan_is_valid(ss, sizeof(*ss)))) {
|
||||
ksnprintf(buf, sizeof(buf), "%p", ss);
|
||||
} else {
|
||||
ksnprintf(buf, bufsize, "{.ss_sp=%p, .ss_flags=%#lx, .ss_size=%'zu}",
|
||||
ss->ss_sp, ss->ss_flags, ss->ss_size);
|
||||
}
|
||||
return buf;
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
|
|
|
@ -20,11 +20,13 @@
|
|||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/ntstdin.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
@ -32,39 +34,49 @@
|
|||
* Implements dup(), dup2(), dup3(), and F_DUPFD for Windows.
|
||||
*/
|
||||
textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
||||
int64_t proc, handle;
|
||||
int64_t rc, proc, handle;
|
||||
|
||||
// validate the api usage
|
||||
if (oldfd < 0) return einval();
|
||||
if (flags & ~O_CLOEXEC) return einval();
|
||||
|
||||
_spinlock(&__fds_lock);
|
||||
|
||||
if (oldfd >= g_fds.n ||
|
||||
(g_fds.p[oldfd].kind != kFdFile && g_fds.p[oldfd].kind != kFdSocket &&
|
||||
g_fds.p[oldfd].kind != kFdConsole)) {
|
||||
_spunlock(&__fds_lock);
|
||||
return ebadf();
|
||||
}
|
||||
|
||||
// allocate a new file descriptor
|
||||
if (newfd == -1) {
|
||||
if ((newfd = __reservefd(start)) == -1) {
|
||||
return -1;
|
||||
for (;;) {
|
||||
if (newfd == -1) {
|
||||
if ((newfd = __reservefd_unlocked(start)) == -1) {
|
||||
_spunlock(&__fds_lock);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (__ensurefds_unlocked(newfd) == -1) {
|
||||
_spunlock(&__fds_lock);
|
||||
return -1;
|
||||
}
|
||||
if (g_fds.p[newfd].kind) {
|
||||
_spunlock(&__fds_lock);
|
||||
close(newfd);
|
||||
_spinlock(&__fds_lock);
|
||||
}
|
||||
if (!g_fds.p[newfd].kind) {
|
||||
g_fds.p[newfd].kind = kFdReserved;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (__ensurefds(newfd) == -1) return -1;
|
||||
if (g_fds.p[newfd].kind) close(newfd);
|
||||
g_fds.p[newfd].kind = kFdReserved;
|
||||
}
|
||||
|
||||
// if this file descriptor is wrapped in a named pipe worker thread
|
||||
// then we should clone the original authentic handle rather than the
|
||||
// stdin worker's named pipe. we won't clone the worker, since that
|
||||
// can always be recreated again on demand.
|
||||
if (g_fds.p[oldfd].worker) {
|
||||
handle = g_fds.p[oldfd].worker->reader;
|
||||
} else {
|
||||
handle = g_fds.p[oldfd].handle;
|
||||
}
|
||||
|
||||
handle = g_fds.p[oldfd].handle;
|
||||
proc = GetCurrentProcess();
|
||||
|
||||
if (DuplicateHandle(proc, handle, proc, &g_fds.p[newfd].handle, 0, true,
|
||||
kNtDuplicateSameAccess)) {
|
||||
g_fds.p[newfd].kind = g_fds.p[oldfd].kind;
|
||||
|
@ -77,12 +89,12 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
|||
} else {
|
||||
g_fds.p[newfd].extra = g_fds.p[oldfd].extra;
|
||||
}
|
||||
if (g_fds.p[oldfd].worker) {
|
||||
g_fds.p[newfd].worker = weaken(RefNtStdinWorker)(g_fds.p[oldfd].worker);
|
||||
}
|
||||
return newfd;
|
||||
rc = newfd;
|
||||
} else {
|
||||
__releasefd(newfd);
|
||||
return __winerr();
|
||||
__releasefd_unlocked(newfd);
|
||||
rc = __winerr();
|
||||
}
|
||||
|
||||
_spunlock(&__fds_lock);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/errno.h"
|
||||
|
||||
int32_t sys_dup3(int32_t oldfd, int32_t newfd, int flags) {
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
|
|
@ -16,44 +16,112 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define ShouldUseMsabiAttribute() 1
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
|
||||
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
|
||||
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
|
||||
|
||||
static noinstrument __msabi bool32
|
||||
BlockExecveConsoleEvent(uint32_t dwCtrlType) {
|
||||
// block SIGINT and SIGQUIT in execve() parent process
|
||||
return true;
|
||||
}
|
||||
|
||||
textwindows int sys_execve_nt(const char *program, char *const argv[],
|
||||
char *const envp[]) {
|
||||
int rc;
|
||||
size_t i;
|
||||
uint32_t dwExitCode;
|
||||
char progbuf[PATH_MAX];
|
||||
struct MemoryIntervals *mm;
|
||||
struct NtStartupInfo startinfo;
|
||||
struct NtProcessInformation procinfo;
|
||||
|
||||
if (strlen(program) + 4 + 1 > PATH_MAX) {
|
||||
return enametoolong();
|
||||
}
|
||||
|
||||
// this is a non-recoverable operation, so do some manual validation
|
||||
if (sys_faccessat_nt(AT_FDCWD, program, X_OK, 0) == -1) {
|
||||
stpcpy(stpcpy(progbuf, program), ".com");
|
||||
if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) {
|
||||
program = progbuf;
|
||||
} else {
|
||||
stpcpy(stpcpy(progbuf, program), ".exe");
|
||||
if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) {
|
||||
program = progbuf;
|
||||
} else {
|
||||
return eacces();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// execve operation is unrecoverable from this point
|
||||
|
||||
// close cloexec handles
|
||||
for (i = 3; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind != kFdEmpty && (g_fds.p[i].flags & O_CLOEXEC)) {
|
||||
__imp_CloseHandle(g_fds.p[i].handle);
|
||||
}
|
||||
}
|
||||
|
||||
bzero(&startinfo, sizeof(startinfo));
|
||||
startinfo.cb = sizeof(struct NtStartupInfo);
|
||||
startinfo.dwFlags = kNtStartfUsestdhandles;
|
||||
startinfo.hStdInput = __getfdhandleactual(0);
|
||||
startinfo.hStdOutput = __getfdhandleactual(1);
|
||||
startinfo.hStdError = __getfdhandleactual(2);
|
||||
|
||||
// spawn the process
|
||||
rc = ntspawn(program, argv, envp, 0, 0, 0, 1, 0, 0, &startinfo, &procinfo);
|
||||
if (rc == -1) return -1;
|
||||
CloseHandle(g_fds.p[0].handle);
|
||||
CloseHandle(g_fds.p[1].handle);
|
||||
CloseHandle(procinfo.hThread);
|
||||
if (rc == -1) {
|
||||
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
|
||||
__imp_ExitProcess(6543);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// zombie shell process remains, to wait for child and propagate its exit
|
||||
// code
|
||||
|
||||
__imp_CloseHandle(g_fds.p[0].handle);
|
||||
__imp_CloseHandle(g_fds.p[1].handle);
|
||||
__imp_CloseHandle(procinfo.hThread);
|
||||
__imp_SetConsoleCtrlHandler((void *)BlockExecveConsoleEvent, 1);
|
||||
do {
|
||||
WaitForSingleObject(procinfo.hProcess, -1);
|
||||
__imp_WaitForSingleObject(procinfo.hProcess, -1);
|
||||
dwExitCode = kNtStillActive;
|
||||
GetExitCodeProcess(procinfo.hProcess, &dwExitCode);
|
||||
__imp_GetExitCodeProcess(procinfo.hProcess, &dwExitCode);
|
||||
} while (dwExitCode == kNtStillActive);
|
||||
CloseHandle(procinfo.hProcess);
|
||||
_Exit(dwExitCode);
|
||||
__imp_CloseHandle(procinfo.hProcess);
|
||||
__imp_ExitProcess(dwExitCode);
|
||||
unreachable;
|
||||
}
|
|
@ -16,28 +16,80 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static bool CanExecute(const char *path) {
|
||||
return !sys_faccessat(AT_FDCWD, path, X_OK, 0);
|
||||
}
|
||||
|
||||
static bool IsApeBinary(const char *path) {
|
||||
int fd;
|
||||
char buf[8];
|
||||
bool res = false;
|
||||
if ((fd = sys_open(path, O_RDONLY, 0)) != -1) {
|
||||
if (sys_read(fd, buf, 8) == 8 && READ64LE(buf) == READ64LE("MZqFpD='")) {
|
||||
res = true;
|
||||
}
|
||||
sys_close(fd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) {
|
||||
size_t n, m;
|
||||
n = strlen(a);
|
||||
m = strlen(b);
|
||||
if (n + 1 + m + 1 < PATH_MAX) {
|
||||
stpcpy(stpcpy(stpcpy(buf, a), "/"), b);
|
||||
return buf;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||
int e;
|
||||
size_t i;
|
||||
char *buf;
|
||||
char **shargs;
|
||||
const char *ape;
|
||||
e = errno;
|
||||
__sys_execve(prog, argv, envp);
|
||||
if (errno != ENOEXEC) return -1;
|
||||
for (i = 0; argv[i];) ++i;
|
||||
shargs = alloca((i + 2) * sizeof(char *));
|
||||
memcpy(shargs + 2, argv + 1, i * sizeof(char *));
|
||||
if (IsFreebsd() || IsNetbsd()) {
|
||||
shargs[0] = firstnonnull(commandv("bash", alloca(PATH_MAX), PATH_MAX),
|
||||
_PATH_BSHELL);
|
||||
buf = alloca(PATH_MAX);
|
||||
shargs = alloca((i + 4) * sizeof(char *));
|
||||
if (IsApeBinary(prog) &&
|
||||
(CanExecute((ape = "/usr/bin/ape")) ||
|
||||
CanExecute(
|
||||
(ape = Join(firstnonnull(getenv("TMPDIR"), "/tmp"), "ape", buf))))) {
|
||||
shargs[0] = ape;
|
||||
shargs[1] = "-";
|
||||
shargs[2] = prog;
|
||||
memcpy(shargs + 3, argv, (i + 1) * sizeof(char *));
|
||||
} else if (CanExecute(prog)) {
|
||||
if (IsFreebsd() || IsNetbsd()) {
|
||||
shargs[0] = firstnonnull(commandv("bash", buf, PATH_MAX), _PATH_BSHELL);
|
||||
} else {
|
||||
shargs[0] = _PATH_BSHELL;
|
||||
}
|
||||
shargs[1] = prog;
|
||||
memcpy(shargs + 2, argv + 1, i * sizeof(char *));
|
||||
} else {
|
||||
shargs[0] = _PATH_BSHELL;
|
||||
return enoexec();
|
||||
}
|
||||
shargs[1] = prog;
|
||||
errno = e;
|
||||
return __sys_execve(shargs[0], shargs, envp);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
@ -63,11 +64,6 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
|
|||
kprintf("})\n");
|
||||
}
|
||||
#endif
|
||||
for (i = 3; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind != kFdEmpty && (g_fds.p[i].flags & O_CLOEXEC)) {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
if (!IsWindows()) {
|
||||
rc = sys_execve(prog, argv, envp);
|
||||
} else {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/files.h"
|
||||
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
|
||||
int sys_fcntl(int fd, int cmd, uintptr_t arg) {
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
|
|
@ -17,24 +17,32 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* Blocks until kernel flushes non-metadata buffers for fd to disk.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @see fsync(), sync_file_range()
|
||||
* @see sync(), fsync(), sync_file_range()
|
||||
* @see __nosync to secretly disable
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int fdatasync(int fd) {
|
||||
int rc;
|
||||
if (!IsWindows()) {
|
||||
rc = sys_fdatasync(fd);
|
||||
if (__nosync != 0x5453455454534146) {
|
||||
if (!IsWindows()) {
|
||||
rc = sys_fdatasync(fd);
|
||||
} else {
|
||||
rc = sys_fdatasync_nt(fd);
|
||||
}
|
||||
STRACE("fdatasync(%d) → %d% m", fd, rc);
|
||||
} else {
|
||||
rc = sys_fdatasync_nt(fd);
|
||||
rc = 0;
|
||||
STRACE("fdatasync(%d) → disabled% m", fd);
|
||||
}
|
||||
STRACE("%s(%d) → %d% m", "fdatasync", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/metastat.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static textwindows bool SubpathExistsThatsNotDirectory(char16_t *path) {
|
||||
int e;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/byhandlefileinformation.h"
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/metastat.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue