Merge branch 'master' into add-antirez-picol

This commit is contained in:
Joseph Stewart 2022-06-27 10:40:00 -07:00 committed by GitHub
commit 62143b9f34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5964 changed files with 689813 additions and 310527 deletions

27
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: build
on:
push:
branches:
- "master"
pull_request:
branches:
- "master"
# run workflow manually from the Actions tab
workflow_dispatch:
jobs:
matrix_on_mode:
runs-on: ubuntu-latest
strategy:
matrix:
mode: ["", tiny, opt, rel]
steps:
- uses: actions/checkout@v3
- name: support ape bins
run: sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
- name: make matrix
run: V=0 make -j2 MODE=${{ matrix.mode }}

1
.gitignore vendored
View file

@ -11,4 +11,3 @@ __pycache__
/TAGS
/bx_enh_dbg.ini
/tool/emacs/*.elc
/usr/share/dict/words

View file

@ -1,6 +0,0 @@
virt: lxd
os: linux
language: c
script: make -j4 V=0
notifications:
email: false

View file

@ -15,7 +15,7 @@
"alignas(x)",
"alignof(x)",
"artificial=",
"nodiscard=",
"dontdiscard=",
"mayalias=",
"forceinline=",
"forcealign(x)=",
@ -48,7 +48,7 @@
"nosideeffect=",
"unreachable=",
"thatispacked=",
"nothrow=",
"dontthrow=",
"nocallback=",
"relegated=",
"hidden=",

View file

@ -37,7 +37,7 @@
#
# # basic debugging
# make -j8 -O MODE=dbg o/dbg/examples/crashreport.com
# o/dbg/examples/crashreport.com
# o/examples/crashreport.com
# less examples/crashreport.c
#
# # extremely tiny binaries
@ -59,9 +59,8 @@
#
# build/config.mk
SHELL = /bin/sh
HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 xnu win7 win10
SANITY := $(shell build/sanitycheck $$PPID)
SHELL = build/bootstrap/cocmd.com
HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 win7 win10 xnu
.SUFFIXES:
.DELETE_ON_ERROR:
@ -69,7 +68,10 @@ SANITY := $(shell build/sanitycheck $$PPID)
.PHONY: all o bins check test depend tags
all: o
o: o/$(MODE)/ape \
o: o/$(MODE)
o/$(MODE): \
o/$(MODE)/ape \
o/$(MODE)/dsp \
o/$(MODE)/net \
o/$(MODE)/libc \
@ -108,13 +110,13 @@ include libc/rand/rand.mk # │
include libc/unicode/unicode.mk # │
include third_party/dlmalloc/dlmalloc.mk #─┘
include libc/mem/mem.mk #─┐
include libc/ohmyplus/ohmyplus.mk # ├──DYNAMIC RUNTIME
include libc/zipos/zipos.mk # │ You can now use stdio
include third_party/gdtoa/gdtoa.mk # │ You can finally call malloc()
include libc/time/time.mk # │
include libc/zipos/zipos.mk # ├──DYNAMIC RUNTIME
include third_party/gdtoa/gdtoa.mk # │ You can now use stdio
include libc/time/time.mk # │ You can finally call malloc()
include libc/thread/thread.mk # │
include libc/alg/alg.mk # │
include libc/stdio/stdio.mk # │
include libc/thread/thread.mk # │
include third_party/libcxx/libcxx.mk # │
include net/net.mk # │
include libc/log/log.mk # │
include third_party/bzip2/bzip2.mk # │
@ -124,6 +126,7 @@ include third_party/stb/stb.mk # │
include dsp/scale/scale.mk # │
include dsp/mpeg/mpeg.mk # │
include dsp/dsp.mk # │
include third_party/zlib/gz/gz.mk # │
include third_party/musl/musl.mk # │
include third_party/getopt/getopt.mk # │
include libc/libc.mk #─┘
@ -134,18 +137,26 @@ include net/http/http.mk # │
include third_party/mbedtls/mbedtls.mk # │
include net/https/https.mk # │
include third_party/regex/regex.mk #─┘
include third_party/tidy/tidy.mk
include third_party/third_party.mk
include libc/testlib/testlib.mk
include tool/viz/lib/vizlib.mk
include tool/args/args.mk
include test/tool/args/test.mk
include third_party/linenoise/linenoise.mk
include third_party/maxmind/maxmind.mk
include third_party/lua/lua.mk
include third_party/make/make.mk
include third_party/finger/finger.mk
include third_party/argon2/argon2.mk
include third_party/smallz4/smallz4.mk
include third_party/sqlite3/sqlite3.mk
include third_party/mbedtls/test/test.mk
include third_party/quickjs/quickjs.mk
include third_party/lz4cli/lz4cli.mk
include third_party/infozip/infozip.mk
include third_party/zip/zip.mk
include third_party/unzip/unzip.mk
include third_party/double-conversion/double-conversion.mk
include tool/build/lib/buildlib.mk
include third_party/chibicc/chibicc.mk
include third_party/chibicc/test/test.mk
@ -155,8 +166,14 @@ include tool/build/emubin/emubin.mk
include tool/build/build.mk
include examples/examples.mk
include examples/pyapp/pyapp.mk
include examples/pylife/pylife.mk
include tool/decode/lib/decodelib.mk
include tool/decode/decode.mk
include tool/lambda/lib/lib.mk
include tool/lambda/lambda.mk
include tool/plinko/lib/lib.mk
include tool/plinko/plinko.mk
include test/tool/plinko/test.mk
include tool/hash/hash.mk
include tool/net/net.mk
include tool/viz/viz.mk
@ -167,6 +184,7 @@ include test/libc/intrin/test.mk
include test/libc/mem/test.mk
include test/libc/nexgen32e/test.mk
include test/libc/runtime/test.mk
include test/libc/thread/test.mk
include test/libc/sock/test.mk
include test/libc/bits/test.mk
include test/libc/str/test.mk
@ -210,12 +228,12 @@ CHECKS = $(foreach x,$(PKGS),$($(x)_CHECKS))
bins: $(BINS)
check: $(CHECKS)
test: $(TESTS) all
test: $(TESTS)
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))
@ -232,11 +250,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
@ -244,12 +262,12 @@ 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 \
LIBC_NT_IPHLPAPI \
LIBC_NT_MSWSOCK \
LIBC_OHMYPLUS \
LIBC_X \
THIRD_PARTY_GETOPT \
LIBC_LOG \
@ -270,7 +288,9 @@ COSMOPOLITAN_OBJECTS = \
LIBC_CALLS \
LIBC_RAND \
LIBC_SYSV_CALLS \
LIBC_NT_KERNELBASE \
LIBC_NT_PSAPI \
LIBC_NT_POWRPROF \
LIBC_NT_PDH \
LIBC_NT_SHELL32 \
LIBC_NT_GDI32 \
LIBC_NT_COMDLG32 \
@ -283,6 +303,7 @@ COSMOPOLITAN_OBJECTS = \
THIRD_PARTY_COMPILER_RT \
LIBC_THREAD \
LIBC_TINYMATH \
THIRD_PARTY_XED \
LIBC_STR \
LIBC_SYSV \
LIBC_INTRIN \
@ -302,11 +323,11 @@ COSMOPOLITAN_HEADERS = \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT \
LIBC_OHMYPLUS \
LIBC_RAND \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
THIRD_PARTY_XED \
LIBC_STR \
LIBC_SYSV \
LIBC_THREAD \
@ -315,11 +336,13 @@ COSMOPOLITAN_HEADERS = \
LIBC_UNICODE \
LIBC_X \
LIBC_ZIPOS \
NET_HTTP \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
THIRD_PARTY_REGEX
o/$(MODE)/cosmopolitan.a: \
@ -335,9 +358,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 \
@ -371,9 +395,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

184
README.md
View file

@ -1,5 +1,6 @@
![Cosmopolitan Honeybadger](usr/share/img/honeybadger.png)
[![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
# Cosmopolitan
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
@ -22,27 +23,67 @@ If you're doing your development work on Linux or BSD then you need just
five files to get started. Here's what you do on Linux:
```sh
wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-1.0.zip
unzip cosmopolitan-amalgamation-1.0.zip
wget https://justine.lol/cosmopolitan/cosmopolitan.zip
unzip cosmopolitan.zip
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:-${HOME:-.}}`
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 +105,116 @@ 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-1.0.tar.gz
tar xf cosmopolitan-1.0.tar.gz # see releases page
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:
```
set host-charset UTF-8
set target-charset UTF-8
set target-wide-charset UTF-8
set osabi none
set complaints 0
set confirm off
set history save on
set history filename ~/.gdb_history
define asm
layout asm
layout reg
end
define src
layout src
layout reg
end
src
```
You normally run the `.com.dbg` file under gdb. If you need to debug the
`.com` file itself, then you can load the debug symbols independently as
```
gdb foo.com -ex 'add-symbol-file foo.com.dbg 0x401000'
```
## Support Vector
| Platform | Min Version | Circa |
@ -89,7 +224,18 @@ find o -name \*.com | xargs ls -rShal | less
| New Technology | Vista | 2006 |
| GNU/Systemd | 2.6.18 | 2007 |
| XNU's Not UNIX! | 15.6 | 2018 |
| FreeBSD | 12 | 2018 |
| FreeBSD | 13 | 2020 |
| OpenBSD | 6.4 | 2018 |
| NetBSD | 9.1 | 2020 |
| GNU Make | 4.0 | 2015 |
| NetBSD | 9.2 | 2021 |
## Special Thanks
Funding for this project is crowdsourced using
[GitHub Sponsors](https://github.com/sponsors/jart) and
[Patreon](https://www.patreon.com/jart). Your support is what makes this
project possible. Thank you! We'd also like to give special thanks to
the following individuals:
- [Joe Drumgoole](https://github.com/jdrumgoole)
For publicly sponsoring our work at the highest tier.

317
ape/ape.S
View file

@ -542,54 +542,83 @@ 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:-${HOME:-.}}/.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.
// We simply printf a 64-byte header and call execve() again.
#else
// The alternative behavior is to copy to $TMPDIR and edit.
// This imposes a variety of caveats of its own that should
// be considered by the user beforehand, such as whether or
// not /tmp is considered trustworthy on a given system, or
// if the administrator chose to mount it with noexec. It's
// up to the user to decide what's best in those situations
// and also note that argv[0] and getauxval(AT_EXECFN) will
// change as a result of this, and lastly note we don't try
// to cleanup the tmp copies for the sake of efficiency. It
// should also be noted that if $0 has directory components
// 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"
// The alternative behavior is to copy to $TMPDIR or $HOME or
// the current directory. We like TMPDIR because it's part of
// the IEEE POSIX standard whereas alternatives (XDG) aren't.
.ascii "t=\"${TMPDIR:-${HOME:-.}}/$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 +627,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,13 +649,21 @@ 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() */
#if SupportsSystemv() || SupportsMetal()
.section .elf.phdrs,"a",@progbits
.long PT_LOAD
.long PF_R|PF_X
.stub ape_rom_offset,quad
@ -668,6 +672,7 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
.stub ape_rom_filesz,quad
.stub ape_rom_memsz,quad
.stub ape_rom_align,quad
.long PT_LOAD
.long PF_R|PF_W
.stub ape_ram_offset,quad
@ -676,14 +681,32 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
.stub ape_ram_filesz,quad
.stub ape_ram_memsz,quad
.stub ape_ram_align,quad
// APE Stack Configuration
//
// We actually respect this when allocating a deterministically
// addressed stack on Windows and other operating systems. However
// there's a few caveats:
//
// - If ape_stack_pf has PF_X then OpenBSD probably won't work
// - We don't bother creating a deterministic stack in MODE=tiny
//
// With that said, here's some examples of configuration:
//
// STATIC_SYMBOL("ape_stack_pf", "7"); // RWX
// STATIC_STACK_ADDR(0x6fffffff0000);
// STATIC_STACK_SIZE(65536);
//
// @see ape.lds for defaults
.long PT_GNU_STACK
.long PF_R|PF_W
.stub ape_stack_offset,quad
.stub ape_stack_vaddr,quad
.stub ape_stack_paddr,quad
.stub ape_stack_filesz,quad
.stub ape_stack_memsz,quad
.stub ape_stack_align,quad
.stub ape_stack_pf,long # override w/ PF_X for execstack
.stub ape_stack_offset,quad # ignored
.stub ape_stack_vaddr,quad # is mmap()'d with MAP_FIXED
.stub ape_stack_paddr,quad # ignored
.stub ape_stack_filesz,quad # ignored
.stub ape_stack_memsz,quad # is mmap(size) argument
.stub ape_stack_align,quad # must be 16+
#if SupportsOpenbsd() || SupportsNetbsd()
.long PT_NOTE
.long PF_R
@ -836,7 +859,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
@ -850,7 +873,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
@ -934,7 +957,7 @@ DLLEXE = DLLSTD
// 6:Contains Initialized Data
// o 5:Contains Code
// rrrr oororrorrr
PETEXT = 0b01110000000000000000000001100000
PETEXT = 0b01100000000000000000000001100000
PEDATA = 0b11000000000000000000000011000000
PEIMPS = 0b11000000000000000000000001000000
@ -1070,18 +1093,12 @@ str.error:
str.crlf:
.asciz "\r\n"
.endobj str.crlf
str.cpuid:
.asciz "cpuid"
.endobj str.cpuid
str.oldskool:
.asciz "oldskool"
.endobj str.oldskool
str.e820:
.asciz "e820"
.endobj str.e820
str.long:
.asciz "nolong"
.endobj str.long
str.oldcpu:
.asciz "oldcpu"
.endobj str.oldcpu
// Serial Line Configuration (8250 UART 16550)
// If it's hacked, it'll at least get hacked very slowly.
@ -1142,47 +1159,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
@ -1264,7 +1240,7 @@ longmodeloader:
lcheck: pushf # check for i8086 / i8088 / i80186
pop %ax
test $0x80,%ah # see intel manual volume 1 20.1.2
jnz 9f # we now assume 32bit is supported
jnz 10f # we now assume 32bit is supported
pushfl # now check for i386 or early i486
pop %eax # test ability to change cpuid bit
mov %eax,%ecx
@ -1275,7 +1251,7 @@ lcheck: pushf # check for i8086 / i8088 / i80186
pushfl
pop %eax
cmp %eax,%ecx
je 12f # we assume cpuid inst is available
je 10f # we assume cpuid inst is available
or %ebx,%eax # puts cpuid bit in the on position
push %eax
popfl
@ -1293,11 +1269,7 @@ lcheck: pushf # check for i8086 / i8088 / i80186
jne 10f
xor %ax,%ax
1: ret
9: mov $REAL(str.oldskool),%di
jmp 20f
10: mov $REAL(str.long),%di
jmp 20f
12: mov $REAL(str.cpuid),%di
10: mov $REAL(str.oldcpu),%di
20: call rldie
.endfn lcheck
@ -1463,6 +1435,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
/*
@ -1498,12 +1511,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]
@ -1519,7 +1529,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
@ -1588,6 +1597,7 @@ ape_pad_text:
.type ape_pad_privileged,@object
.hidden ape_pad_privileged
ape_pad_privileged:
.align 4096
.previous
.section .ape.pad.rodata,"a",@progbits
@ -1615,5 +1625,26 @@ ape_idata_ro:
__data_start:
.previous
.section .dataepilogue,"aw",@progbits
.type __data_end,@object
.globl __data_end
.hidden __data_end
__data_end:
.previous
.section .bssprologue,"aw",@nobits
.type __bss_start,@object
.globl __bss_start
.hidden __bss_start
__bss_start:
.previous
.section .bssepilogue,"aw",@nobits
.type __bss_end,@object
.globl __bss_end
.hidden __bss_end
__bss_end:
.previous
.end


View file

@ -177,16 +177,20 @@
#include "ape/macros.internal.h"
#include "ape/relocations.h"
#include "libc/dce.h"
#include "libc/elf/def.h"
#include "libc/elf/pf2prot.internal.h"
#include "libc/nt/pedef.internal.h"
#include "libc/zip.h"
ENTRY(_start)
PHDRS {
Head PT_LOAD FLAGS(5);
Rom PT_LOAD FLAGS(5);
Ram PT_LOAD FLAGS(6);
stack PT_GNU_STACK FLAGS(6);
Head PT_LOAD FLAGS(PF_X|PF_R);
Rom PT_LOAD FLAGS(PF_X|PF_R);
Ram PT_LOAD FLAGS(PF_W|PF_R);
Tls PT_TLS FLAGS(PF_W|PF_R);
Bss PT_LOAD FLAGS(PF_W|PF_R);
stack PT_GNU_STACK FLAGS(PF_W|PF_R);
}
SECTIONS {
@ -241,10 +245,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)
@ -281,6 +287,7 @@ SECTIONS {
/* Privileged code invulnerable to magic */
KEEP(*(.ape.pad.privileged));
. += . > 0 ? 1 : 0;
/*END: morphable code */
HIDDEN(__privileged_start = .);
. += . > 0 ? 1 : 0;
*(.privileged)
@ -343,21 +350,10 @@ SECTIONS {
/*END: Read Only Data */
} :Rom
.tdata . : {
_tdata_start = .;
*(SORT_BY_ALIGNMENT(.tdata))
*(SORT_BY_ALIGNMENT(.tdata.*))
_tdata_end = .;
}
.tbss . : {
_tbss_start = .;
*(SORT_BY_ALIGNMENT(.tbss))
*(SORT_BY_ALIGNMENT(.tbss.*))
_tbss_end = .;
}
.data . : {
.data ALIGN(PAGESIZE) : {
/*BEGIN: Read/Write Data */
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)))
/*BEGIN: NT FORK COPYING */
KEEP(*(.dataprologue))
*(.data .data.*)
KEEP(*(SORT_BY_NAME(.sort.data.*)))
@ -378,32 +374,39 @@ SECTIONS {
. = ALIGN(__SIZEOF_POINTER__);
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
KEEP(*(.piro.pad.data))
. = ALIGN(PAGESIZE);
KEEP(*(.dataepilogue))
/*END: NT FORK COPYING */
HIDDEN(_edata = .);
PROVIDE_HIDDEN(edata = .);
} :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 . : {
KEEP(*(SORT_BY_NAME(.zip.*)))
HIDDEN(_ezip = .);
}
} :Ram
.tdata . : {
_tdata_start = .;
*(SORT_BY_ALIGNMENT(.tdata))
*(SORT_BY_ALIGNMENT(.tdata.*))
_tdata_end = .;
. = ALIGN(PAGESIZE);
} :Tls
. = ALIGN(PAGESIZE);
/*END: file content that's loaded by o/s */
/*BEGIN: bss memory void */
.tbss . : {
_tbss_start = .;
*(SORT_BY_ALIGNMENT(.tbss))
*(SORT_BY_ALIGNMENT(.tbss.*))
_tbss_end = .;
} :Tls
/*END: file content */
/*BEGIN: bss memory that's addressable */
.bss ALIGN(64) : {
/*BEGIN: NT FORK COPYING */
KEEP(*(.bssprologue))
KEEP(*(SORT_BY_NAME(.piro.bss.init.*)))
*(.piro.bss)
KEEP(*(SORT_BY_NAME(.piro.bss.sort.*)))
@ -418,10 +421,12 @@ SECTIONS {
KEEP(*(SORT_BY_NAME(.sort.bss.*)))
KEEP(*(.bssepilogue))
/*END: NT FORK COPYING */
. = ALIGN(FRAMESIZE); /* for brk()/sbrk() allocation */
HIDDEN(_end = .);
PROVIDE_HIDDEN(end = .);
}
} :Bss
/*END: nt addressability guarantee */
/*END: bsd addressability guarantee */
@ -477,6 +482,9 @@ PFSTUB4(ape_elf_phnum, (ape_phdrs_end - ape_phdrs) / 56);
PFSTUB4(ape_elf_shnum, 0);
PFSTUB4(ape_elf_shstrndx, 0);
HIDDEN(_tdata_size = _tdata_end - _tdata_start);
HIDDEN(_tls_size = _tbss_end - _tdata_start);
HIDDEN(__privileged_addr = ROUNDDOWN(__privileged_start, PAGESIZE));
HIDDEN(__privileged_size = (ROUNDUP(__privileged_end, PAGESIZE) -
ROUNDDOWN(__privileged_start, PAGESIZE)));
@ -492,13 +500,15 @@ HIDDEN(ape_rom_rva = RVA(ape_rom_vaddr));
HIDDEN(ape_ram_offset = ape_rom_offset + ape_rom_filesz);
HIDDEN(ape_ram_vaddr = ADDR(.data));
HIDDEN(ape_ram_paddr = LOADADDR(.data));
HIDDEN(ape_ram_filesz = SIZEOF(.data));
HIDDEN(ape_ram_filesz = SIZEOF(.data) + SIZEOF(.tdata));
HIDDEN(ape_ram_memsz = ADDR(.bss) + SIZEOF(.bss) - ape_ram_vaddr);
HIDDEN(ape_ram_align = PAGESIZE);
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);
@ -522,8 +532,8 @@ HIDDEN(ape_text_rva = RVA(ape_text_vaddr));
HIDDEN(ape_data_offset = ape_ram_offset + LOADADDR(.data) - ape_ram_paddr);
HIDDEN(ape_data_paddr = LOADADDR(.data));
HIDDEN(ape_data_vaddr = ADDR(.data));
HIDDEN(ape_data_filesz = SIZEOF(.data));
HIDDEN(ape_data_memsz = SIZEOF(.data));
HIDDEN(ape_data_filesz = SIZEOF(.data) + SIZEOF(.tdata));
HIDDEN(ape_data_memsz = SIZEOF(.data) + SIZEOF(.tdata));
HIDDEN(ape_data_align = PAGESIZE);
HIDDEN(ape_data_rva = RVA(ape_data_vaddr));
@ -534,34 +544,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);
#if SupportsXnu()
SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8);
SHSTUB2(ape_macho_dd_count, (ape_macho_end - ape_macho) / 8);
#endif
#if SupportsWindows() || SupportsMetal()
#define LINK_WINDOWS (SupportsWindows() && !DEFINED(EfiMain))
PFSTUB4(ape_pe_offset, ape_pe - ape_mz);
HIDDEN(ape_pe_optsz = ape_pe_sections - (ape_pe + 24));
HIDDEN(ape_pe_shnum = (ape_pe_sections_end - ape_pe_sections) / 40);
HIDDEN(ape_pe_base = IMAGE_BASE_VIRTUAL);
HIDDEN(ape_idata = LINK_WINDOWS ? RVA(ape_idata_iat) : 0);
HIDDEN(ape_idata_iatsize = LINK_WINDOWS ? ape_idata_iatend - ape_idata_iat : 0);
HIDDEN(ape_idata = LINK_WINDOWS ? RVA(ape_idata_idt) : 0);
HIDDEN(ape_idata_idtsize = LINK_WINDOWS ? ape_idata_idtend - ape_idata_idt : 0);
HIDDEN(v_ntversion = LINK_WINDOWS ? 6 : 1);
HIDDEN(v_ntdllchar = LINK_WINDOWS ? 288 : 0);
HIDDEN(v_ntsubversion = LINK_WINDOWS ? 6 : 5);
HIDDEN(v_ntsubsystem = (LINK_WINDOWS
? (DEFINED(GetMessage)
? kNtImageSubsystemWindowsGui
: kNtImageSubsystemWindowsCui)
: kNtImageSubsystemEfiApplication));
HIDDEN(ape_pe_entry = LINK_WINDOWS ? WinMain : EfiMain);
#endif
/* 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 SupportsMetal()
HIDDEN(v_ape_realsectors =
@ -669,6 +659,32 @@ CHURN(WinMain);
#endif /* SupportsWindows() */
#endif /* SupportsXnu() */
#if SupportsWindows() || SupportsMetal()
#define LINK_WINDOWS (SupportsWindows() && !DEFINED(EfiMain))
PFSTUB4(ape_pe_offset, ape_pe - ape_mz);
HIDDEN(ape_pe_optsz = ape_pe_sections - (ape_pe + 24));
HIDDEN(ape_pe_shnum = (ape_pe_sections_end - ape_pe_sections) / 40);
HIDDEN(ape_pe_base = IMAGE_BASE_VIRTUAL);
HIDDEN(ape_idata = LINK_WINDOWS ? RVA(ape_idata_iat) : 0);
HIDDEN(ape_idata_iatsize = LINK_WINDOWS ? ape_idata_iatend - ape_idata_iat : 0);
HIDDEN(ape_idata = LINK_WINDOWS ? RVA(ape_idata_idt) : 0);
HIDDEN(ape_idata_idtsize = LINK_WINDOWS ? ape_idata_idtend - ape_idata_idt : 0);
HIDDEN(v_ntversion = LINK_WINDOWS ? 6 : 1);
HIDDEN(v_ntdllchar = LINK_WINDOWS ? 288 : 0);
HIDDEN(v_ntsubversion = LINK_WINDOWS ? 6 : 5);
HIDDEN(v_ntsubsystem = (LINK_WINDOWS
? (DEFINED(GetMessage)
? kNtImageSubsystemWindowsGui
: kNtImageSubsystemWindowsCui)
: kNtImageSubsystemEfiApplication));
HIDDEN(ape_pe_entry = LINK_WINDOWS ? WinMain : EfiMain);
#endif
#if SupportsXnu()
SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8);
SHSTUB2(ape_macho_dd_count, (ape_macho_end - ape_macho) / 8);
#endif
ASSERT(DEFINED(ape_mz) ? ape_mz == IMAGE_BASE_VIRTUAL : 1, "linker panic");
ASSERT((DEFINED(__init_bss_end) ? __init_bss_end : 0) % __SIZEOF_POINTER__ == 0,
"__init_bss misalign");
@ -685,6 +701,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) */

View file

@ -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
View 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
View 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 PAGESIZE # 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 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
.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
View 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
View 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;

View file

@ -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
View 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_ */

View file

@ -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;

View file

@ -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

Binary file not shown.

BIN
build/bootstrap/apetest.com Executable file

Binary file not shown.

Binary file not shown.

BIN
build/bootstrap/cocmd.com Executable file

Binary file not shown.

Binary file not shown.

BIN
build/bootstrap/cp.com Executable file

Binary file not shown.

BIN
build/bootstrap/echo.com Executable file

Binary file not shown.

BIN
build/bootstrap/gzip.com Executable file

Binary file not shown.

BIN
build/bootstrap/make.com Executable file

Binary file not shown.

Binary file not shown.

BIN
build/bootstrap/mkdir.com Executable file

Binary file not shown.

Binary file not shown.

BIN
build/bootstrap/pwd.com Executable file

Binary file not shown.

BIN
build/bootstrap/rm.com Executable file

Binary file not shown.

BIN
build/bootstrap/touch.com Executable file

Binary file not shown.

BIN
build/bootstrap/unbundle.com Executable file

Binary file not shown.

Binary file not shown.

View file

@ -5,6 +5,7 @@
#
# - `make`
# - Backtraces
# - Syscall tracing
# - Function tracing
# - Reasonably small
# - Reasonably optimized
@ -13,6 +14,7 @@ ifeq ($(MODE),)
CONFIG_CCFLAGS += \
$(BACKTRACES) \
$(FTRACE) \
-DSYSDEBUG \
-Og
TARGET_ARCH ?= \
-msse3
@ -23,6 +25,8 @@ endif
# - `make MODE=opt`
# - Backtraces
# - More optimized
# - Syscall tracing
# - Function tracing
# - Reasonably small
# - No memory corruption detection
# - assert() / CHECK_xx() may leak code into binary for debuggability
@ -35,7 +39,9 @@ CONFIG_CPPFLAGS += \
CONFIG_CCFLAGS += \
$(BACKTRACES) \
$(FTRACE) \
-O3
-DSYSDEBUG \
-O3 \
-fmerge-all-constants
TARGET_ARCH ?= \
-march=native
endif
@ -55,7 +61,7 @@ CONFIG_CPPFLAGS += \
-Wa,-msse2avx \
-DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += \
-O3
-O3 -fmerge-all-constants
DEFAULT_COPTS += \
-mred-zone
TARGET_ARCH ?= \
@ -122,10 +128,12 @@ CONFIG_CPPFLAGS += \
CONFIG_CCFLAGS += \
$(BACKTRACES) \
$(FTRACE) \
-DSYSDEBUG \
-O2 \
-fno-inline
CONFIG_COPTS += \
-fsanitize=address
-fsanitize=address \
-fsanitize=undefined
TARGET_ARCH ?= \
-msse3
OVERRIDE_CCFLAGS += \
@ -287,7 +295,7 @@ endif
# LLVM Mode
ifeq ($(MODE), llvm)
TARGET_ARCH ?= -msse3
CONFIG_CCFLAGS += $(BACKTRACES) $(FTRACE) -O2
CONFIG_CCFLAGS += $(BACKTRACES) $(FTRACE) -DSYSDEBUG -O2
AS = clang
CC = clang
CXX = clang++

View file

@ -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 -P4096 $(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
@ -100,7 +121,8 @@ SANITIZER = \
NO_MAGIC = \
-mno-fentry \
-fno-stack-protector \
-fwrapv
-fwrapv \
-fno-sanitize=all
OLD_CODE = \
-fno-strict-aliasing \
@ -114,7 +136,7 @@ TRADITIONAL = \
DEFAULT_CCFLAGS = \
-Wall \
-Werror \
-fdebug-prefix-map="$(PWD)"= \
-fdebug-prefix-map='$(PWD)'= \
-frecord-gcc-switches
DEFAULT_OFLAGS = \
@ -148,7 +170,6 @@ DEFAULT_CFLAGS = \
-std=gnu2x
DEFAULT_CXXFLAGS = \
-std=gnu++11 \
-fno-rtti \
-fno-exceptions \
-fuse-cxa-atexit \
@ -169,7 +190,7 @@ DEFAULT_LDFLAGS = \
--gc-sections \
--build-id=none \
--no-dynamic-linker \
-zmax-page-size=0x1000
-zmax-page-size=0x1000 #--cref -Map=$@.map
ZIPOBJ_FLAGS = \
-b$(IMAGE_BASE_VIRTUAL)
@ -180,7 +201,7 @@ PYFLAGS = \
ASONLYFLAGS = \
-c \
-g \
--debug-prefix-map="$(PWD)"=
--debug-prefix-map='$(PWD)'=
DEFAULT_LDLIBS =
@ -301,6 +322,9 @@ OBJECTIFY.greg.c = \
-fno-instrument-functions \
-fno-optimize-sibling-calls \
-fno-sanitize=all \
-ffreestanding \
-mno-fentry \
-fwrapv \
-c
OBJECTIFY.ansi.c = $(CC) $(OBJECTIFY.c.flags) -ansi -Wextra -Werror -pedantic-errors -c

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -43,12 +43,16 @@
# '(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' "$@"
# ctags doesn't understand function prototypes, e.g.
# bool isheap(void *p) nothrow nocallback;
# bool isheap(void *p) dontthrow nocallback;
set -- --regex-c='/^[_[:alpha:]][_[:alnum:]]*[ *][ *]*\([_[:alpha:]][_[:alnum:]]*[ *][ *]*\)*\([_[:alpha:]][_$[:alnum:]]*\)(.*/\2/b' "$@"
# ctags doesn't understand function pointers, e.g.

View file

@ -29,9 +29,8 @@ 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/%.h.okk: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -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) $<
o/%.zip.o: o/% ; @$(COMPILE) -AZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
@ -60,7 +59,8 @@ o/$(MODE)/%.o: %.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx
o/$(MODE)/%.o: o/$(MODE)/%.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $<
o/$(MODE)/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
o/$(MODE)/%.h.ok: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
o/$(MODE)/%.h.okk: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
o/$(MODE)/%.hh.ok: %.hh ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
o/$(MODE)/%.okk: % ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
o/$(MODE)/%.cxx.o: %.c ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) -xc++ $(OUTPUT_OPTION) $<
o/$(MODE)/%.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.greg.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
@ -74,16 +74,25 @@ 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) $<
o/$(MODE)/%-clang.asm: %.c ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
o/$(MODE)/%-clang.asm: %.cc ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
o/$(MODE)/%-clang.asm: CC = $(CLANG)
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 $@ $<

View file

@ -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

View file

@ -1,9 +0,0 @@
MZqFpD=123
exit $MZqFpD

View file

@ -37,4 +37,3 @@ sad16x8n:
jnz 0b
1: .leafepilogue
.endfn sad16x8n,globl,hidden
.source __FILE__

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
.source __FILE__
clamp4int256$core:
.leafprologue

View file

@ -72,7 +72,8 @@ static const float PLM_VIDEO_PIXEL_ASPECT_RATIO[] = {
0.6735, /* 3:4? */
0.7031, /* MPEG-1 / MPEG-2 video encoding divergence? */
0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815,
1.0255, 1.0695, 1.0950, 1.1575, 1.2051};
1.0255, 1.0695, 1.0950, 1.1575, 1.2051,
};
static const float PLM_VIDEO_PICTURE_RATE[] = {
23.976, /* NTSC-Film */

View file

@ -101,7 +101,7 @@ struct SamplingSolution *ComputeSamplingSolution(long dn, long sn, double dar,
if (!off) off = (dar - 1) / 2;
f = dar < 1 ? 1 / dar : dar;
s = 3 * f + 4;
fweights = gc(xcalloc(s, sizeof(double)));
fweights = gc(xcalloc(s + /*xxx*/ 2, sizeof(double)));
res = NewSamplingSolution(dn, s);
weights = res->weights;
indices = res->indices;

View file

@ -19,6 +19,7 @@
#include "dsp/tty/tty.h"
#include "libc/bits/pushpop.h"
#include "libc/dce.h"
#include "libc/log/internal.h"
#include "libc/log/log.h"
#include "libc/nt/console.h"
#include "libc/nt/runtime.h"
@ -30,7 +31,7 @@
static int ttysetcursor(int fd, bool visible) {
struct NtConsoleCursorInfo ntcursor;
char code[8] = "\e[?25l";
if (IsTerminalInarticulate()) return 0;
if (__nocolor) return 0;
if (visible) code[5] = 'h';
if (SupportsWindows()) {
GetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor);

View file

@ -31,13 +31,6 @@ int ttysetraw(struct termios *conf, int64_t flags) {
conf->c_cflag &= ~(CSIZE | PARENB);
conf->c_cflag |= CS8;
conf->c_iflag |= IUTF8;
/* if (flags & kTtyLfToCrLf) { */
/* /\* conf->c_oflag &= ~(OLCUC | OCRNL | ONLRET | OFILL | OFDEL); *\/ */
/* /\* conf->c_oflag |= ONLCR | ONOCR; *\/ */
/* conf->c_oflag |= ONLCR; */
/* } else { */
/* conf->c_oflag &= ~OPOST; */
/* } */
if (!(flags & kTtySigs)) {
conf->c_iflag &= ~(IGNBRK | BRKINT);
conf->c_lflag &= ~(ISIG);

View file

@ -731,19 +731,19 @@ static bool ChunkEq(struct TtyRgb c[hasatleast 4],
}
static struct TtyRgb *CopyChunk(struct TtyRgb chunk[hasatleast 4],
const struct TtyRgb *c, size_t n) {
chunk[TL] = c[0 + 0];
chunk[TR] = c[0 + 1];
chunk[BL] = c[n + 0];
chunk[BR] = c[n + 1];
const struct TtyRgb *c, size_t xn) {
chunk[TL] = c[00 + 0];
chunk[TR] = c[00 + 1];
chunk[BL] = c[xn + 0];
chunk[BR] = c[xn + 1];
return chunk;
}
static dontinline char *CopyRun(char *v, size_t n,
struct TtyRgb lastchunk[hasatleast 4],
const struct TtyRgb **c, size_t *x,
struct TtyRgb *bg, struct TtyRgb *fg,
struct Glyph *glyph) {
struct TtyRgb lastchunk[hasatleast 4],
const struct TtyRgb **c, size_t *x,
struct TtyRgb *bg, struct TtyRgb *fg,
struct Glyph *glyph) {
struct TtyRgb chunk[4];
if (memcmp(glyph, &kGlyphs[1][0], sizeof(*glyph)) == 0) {
if (!ttyeq(*bg, *fg)) {
@ -766,17 +766,18 @@ static dontinline char *CopyRun(char *v, size_t n,
/**
* Maps 2×2 pixel chunks onto ANSI UNICODE cells.
* @note h/t Nick Black for his quadrant blitting work on notcurses
* @note yn and xn need to be even
*/
char *ttyraster(char *v, const struct TtyRgb *c, size_t yn, size_t n,
char *ttyraster(char *v, const struct TtyRgb *c, size_t yn, size_t xn,
struct TtyRgb bg, struct TtyRgb fg) {
unsigned y, x;
struct Pick p;
struct Glyph glyph;
struct TtyRgb chun[4], lastchunk[4];
for (y = 0; y < yn; y += 2, c += n) {
for (y = 0; y < yn; y += 2, c += xn) {
if (y) *v++ = '\r', *v++ = '\n';
for (x = 0; x < n; x += 2, c += 2) {
CopyChunk(chun, c, n);
for (x = 0; x < xn; x += 2, c += 2) {
CopyChunk(chun, c, xn);
if (ttyquant()->alg == kTtyQuantTrue) {
if (ttyquant()->blocks == kTtyBlocksCp437) {
p = PickBlockCp437True(chun[TL], chun[TR], chun[BL], chun[BR]);

View file

@ -19,7 +19,6 @@
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/termios.h"

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
.source __FILE__
// Returns index of minimum uint16 in array.
//

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
.source __FILE__
// Returns index of minimum positive int16 in array.
//

View file

@ -18,7 +18,6 @@
*/
#include "libc/nexgen32e/x86feature.h"
#include "libc/macros.internal.h"
.source __FILE__
// Dispatches to fastest windex() implementation.
.initbss 300,_init_windex

View file

@ -18,9 +18,12 @@
* make -j12 o//examples/auto-launch-gdb-on-crash.com
* o//examples/auto-launch-gdb-on-crash.com
*
* Backtrace is logged instead if run outside interactive terminal.
* Environmental factors such as GDB, MAKEFLAGS, ADDR2LINE, TERM, and
* isatty(STDERR_FILENO) should also be taken into consideration:
* Backtrace is logged instead if run outside interactive terminal. We
* also don't auto-launch GDB on non-Linux since the development tools
* on other platforms generally can't be relied upon to correctly debug
* binaries built with a Linux toolchain. Environmental factors such as
* GDB, MAKEFLAGS, ADDR2LINE, TERM, and isatty(STDERR_FILENO) should
* also be taken into consideration:
*
* $ export GDB=eoatuhshtuone
* $ o//examples/auto-launch-gdb-on-crash.com
@ -37,7 +40,7 @@
*/
int main(int argc, char *argv[]) {
showcrashreports();
ShowCrashReports();
asm("int3"); /* cf. __die(), perror("msg"), abort(), exit(1), _Exit(1) */
return 0;
}

View file

@ -0,0 +1,62 @@
#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/bits/bits.h"
#include "libc/dce.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
/**
* ASAN static memory safety crash example.
*
* make -j8 MODE=dbg o/dbg/examples/auto-memory-safety-crash.com
* o/dbg/examples/auto-memory-safety-crash.com
*
* You should see:
*
* global redzone 1-byte store at 0x42700d shadow 0x8007ce01
* ./o/dbg/examples/auto-memory-safety-crash.com
* x
* ........................................GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
* |0 |0 |0 |0 |5 |-18 |-18 |-18 |-18
*  f.ä     f.ä     fD  hello                                                
* 000000400000-000000427000 .text
* 000000427000-000000429000 .data address
* 00007fff0000-00008000ffff
* 000080070000-00008008ffff shadow
* 0e007fff0000-0e008000ffff
* 100047d20000-100047d3ffff
* 6ffffffe0000-6fffffffffff
* the memory in question belongs to the symbols
* buffer [0x427000,0x42700c] size 13
* the crash was caused by
* 0x00000000004046f3: __die at libc/log/die.c:40
* 0x0000000000404aed: __asan_report_store at libc/intrin/asan.c:1183
* 0x0000000000402552: main at examples/auto-memory-safety-crash.c:27
* 0x000000000040268d: cosmo at libc/runtime/cosmo.S:64
* 0x00000000004021ae: _start at libc/crt/crt.S:77
*
* @see libc/intrin/asancodes.h for meaning of G, etc. and negative numbers
* @see libc/nexgen32e/kcp437.S for meaning of symbols
*/
char buffer[13] = "hello";
int main(int argc, char *argv[]) {
if (!IsAsan()) {
printf("this example is intended for MODE=asan or MODE=dbg\n");
exit(1);
}
ShowCrashReports(); /* not needed but yoinks appropriate symbols */
int i = 13;
asm("" : "+r"(i)); /* prevent compiler being smart */
buffer[i] = 1;
return 0;
}

View file

@ -0,0 +1,74 @@
#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/bits/bits.h"
#include "libc/dce.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* ASAN heap memory safety crash example.
*
* make -j8 MODE=dbg o/dbg/examples/auto-memory-safety-crash2.com
* o/dbg/examples/auto-memory-safety-crash2.com
*
* You should see:
*
* heap overrun 1-byte store at 0x10008004002d shadow 0x20090000005
* ./o/dbg/examples/auto-memory-safety-crash2.com
* x
* OOOOOOOOOOOUUUUUUUUUUUUUUUU.............OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* |-7 |-6 |-6 |0 |5 |-7 |-7 |-7 |-7
*    »!@     ÿ:Y= S       hello                 GT&@     á+@     »!@  
* 000000400000-00000042b000 .text
* 00000042b000-00000042d000 .data
* 00007fff0000-00008000ffff
* 000080070000-00008008ffff
* 02008fff0000-02009000ffff shadow
* 0e007fff0000-0e008000ffff
* 10003ab90000-10003abaffff
* 100080000000-10008000ffff address
* 6ffffffe0000-6fffffffffff
*
* the memory was allocated by
* 0x100080040020 64 bytes [dlmalloc]
* 0x100080040030 13 bytes [actual]
* 402608 main
* 402ba0 cosmo
* 4021af _start
*
* the crash was caused by
* 0x0000000000404793: __die at libc/log/die.c:40
* 0x0000000000404f56: __asan_report_store at libc/intrin/asan.c:1183
* 0x0000000000402579: main at examples/auto-memory-safety-crash2.c:30
* 0x000000000040270f: cosmo at libc/runtime/cosmo.S:64
* 0x00000000004021ae: _start at libc/crt/crt.S:77
*
* @see libc/intrin/asancodes.h for meaning of U, O, etc. and negative numbers
* @see libc/nexgen32e/kcp437.S for meaning of symbols
*/
int main(int argc, char *argv[]) {
if (!IsAsan()) {
printf("this example is intended for MODE=asan or MODE=dbg\n");
exit(1);
}
char *buffer;
ShowCrashReports(); /* not needed but yoinks appropriate symbols */
buffer = malloc(13);
strcpy(buffer, "hello");
int i = 13;
asm("" : "+r"(i)); /* prevent compiler being smart */
buffer[i] = 1;
asm("" : "+r"(buffer)); /* prevent compiler being smart */
return 0;
}

View file

@ -0,0 +1,39 @@
#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/bits/bits.h"
#include "libc/dce.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* ASAN use-after-free memory safety crash example.
*
* make -j8 MODE=dbg o/dbg/examples/auto-memory-safety-crash3.com
* o/dbg/examples/auto-memory-safety-crash3.com
*
*/
int main(int argc, char *argv[]) {
if (!IsAsan()) {
printf("this example is intended for MODE=asan or MODE=dbg\n");
exit(1);
}
char *buffer;
ShowCrashReports(); /* not needed but yoinks appropriate symbols */
buffer = malloc(13);
strcpy(buffer, "hello");
free(buffer);
asm("" : "+r"(buffer)); /* prevent compiler being smart */
buffer[0] = 1;
return 0;
}

View file

@ -7,19 +7,30 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/intrin/kprintf.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/stdio/stdio.h"
int main(int argc, char *argv[]) {
showcrashreports();
ShowCrashReports();
if (IsDebuggerPresent(false)) {
printf("debugger found!\r\n");
DebugBreak();
return 0;
kprintf("debugger found!%n");
} else {
kprintf("try running: gdb %s%n", argv[0]);
kprintf("try running: o//tool/build/strace.com %s%n", argv[0]);
}
printf("try running: gdb %s\r\n", argv[0]);
return 1;
asm volatile("mov\t%4,%%r10\n\t"
"mov\t%5,%%r8\n\t"
"mov\t%6,%%r9\n\t"
"int3"
: /* no outputs */
: "a"(0), "D"(1), "S"(2), "d"(3), "g"(4), "g"(5), "g"(6)
: "r8", "r9", "r10");
printf("recovered from SIGTRAP without handler\r\n");
return 0;
}

88
examples/check.c Normal file
View file

@ -0,0 +1,88 @@
#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/log/check.h"
/**
* @fileoverview Check Macros
*
* The simplest assertion is:
*
* assert(123 == x);
*
* This has some downsides:
*
* 1. It's nice to know what `x` is when it crashes
* 2. It's sometimes nice to have the check always take effect.
* 3. It's problematic that assert() can't do __builtin_assume()
*
* Cosmopolitan provides alternative macros like:
*
* - `CHECK(EXPR, ...)`
* - `CHECK_EQ(WANT, GOT, ...)`
* - `CHECK_NE(WANT, GOT, ...)`
* - `CHECK_GT(WANT, GOT, ...)`
* - `CHECK_LT(WANT, GOT, ...)`
* - `CHECK_NOTNULL(EXPR, ...)`
*
* The CHECK macros always happen. They always kill the process when
* they fail. Printf formatting information may be provided as extra
* arguments. On the other hand, there exists:
*
* - `DCHECK(EXPR, ...)`
* - `DCHECK_EQ(WANT, GOT, ...)`
* - `DCHECK_NE(WANT, GOT, ...)`
* - `DCHECK_GT(WANT, GOT, ...)`
* - `DCHECK_LT(WANT, GOT, ...)`
* - `DCHECK_NOTNULL(EXPR, ...)`
*
* The DCHECK macros always happen when NDEBUG isn't defined. When
* NDEBUG is defined, they still happen, but in a special way that
* causes the compiler to recognize their failure as undefined behavior.
* What this means is that, if the provided expressions are pure without
* side-effects, then the code compiles down to nothing and the compiler
* may be able to use the knowledge of something being the case in order
* to optimize other code adjacent to your DCHECK.
*
* In the default build modes, this prints lots of information:
*
* error:examples/check.c:23:check.com: check failed on nightmare pid 15412
* CHECK_EQ(123, some_source_code);
* 0x7b (123)
* == 0x64 (some_source_code)
* extra info: hello
* EUNKNOWN/0/No error information
* ./o//examples/check.com
* 0x0000000000407404: __die at libc/log/die.c:42
* 0x0000000000407340: __check_fail at libc/log/checkfail.c:73
* 0x00000000004071d0: main at examples/check.c:23
* 0x000000000040256e: cosmo at libc/runtime/cosmo.S:69
* 0x000000000040217d: _start at libc/crt/crt.S:85
*
* In NDEBUG mode (e.g. MODE=rel, MODE=tiny, etc.) this prints a much
* simpler message that, most importantly, doesn't include any source
* code, although it still includes the file name for reference.
*
* error:examples/check.c:14: check failed: 123 == 100: extra info: hello
*
* That way your release binaries won't leak CI. You may optionally link
* the following functions to further expand the information shown by
* the NDEBUG check failure:
*
* STATIC_YOINK("__die");
* STATIC_YOINK("strerror");
*
* Please note that backtraces aren't ever available in MODE=tiny.
*/
int main(int argc, char *argv[]) {
int some_source_code = 100;
CHECK_EQ(123, some_source_code, "extra info: %s", "hello");
return 0;
}

192
examples/compress.c Normal file
View file

@ -0,0 +1,192 @@
#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/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 "third_party/getopt/getopt.h"
#include "third_party/zlib/zlib.h"
#define CHUNK 32768
#define USAGE \
" <STDIN >STDOUT\n\
\n\
SYNOPSIS\n\
\n\
Zlib Compressor\n\
\n\
FLAGS\n\
\n\
-?\n\
-h help\n\
-0 disable compression\n\
-1 fastest compression\n\
-4 coolest compression\n\
-9 maximum compression\n\
-F fixed strategy (advanced)\n\
-L filtered strategy (advanced)\n\
-R run length strategy (advanced)\n\
-H huffman only strategy (advanced)\n\
\n"
// 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 1 9); do
for strategy in F L R H; do
o/$m/examples/compress.com -$strategy$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 </tmp/comp | dd count=10000 2>/tmp/info >/dev/null
decompspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info)
size=$(o/$m/examples/compress.com -$strategy$level <o/$m/examples/compress.com | wc -c)
echo "level $strategy $level $size compress $compspeed decompress $decompspeed"
done
done
*/
// clang-format on
int level;
int strategy;
wontreturn void PrintUsage(int rc, FILE *f) {
fputs("usage: ", f);
fputs(program_invocation_name, f);
fputs(USAGE, f);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?hFLRH0123456789")) != -1) {
switch (opt) {
case 'F':
strategy = Z_FIXED;
break;
case 'L':
strategy = Z_FILTERED;
break;
case 'R':
strategy = Z_RLE;
break;
case 'H':
strategy = Z_HUFFMAN_ONLY;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
level = opt - '0';
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
int compressor(int infd, int outfd) {
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, 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 = Z_DEFAULT_COMPRESSION;
strategy = Z_DEFAULT_STRATEGY;
GetOpts(argc, argv);
rc = compressor(0, 1);
if (rc == Z_OK) {
return 0;
} else {
fprintf(stderr, "error: compressor: %s\n", zerr(rc));
return 1;
}
}

316
examples/cosh.c Normal file
View file

@ -0,0 +1,316 @@
#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/struct/dirent.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#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"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/append.internal.h"
#include "libc/stdio/stdio.h"
#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 Cosmopolitan Shell
*
* This doesn't have script language features like UNBOURNE.COM but it
* 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;
for (i = 0; i < c->len; ++i) {
if (!strcmp(s, c->cvec[i])) {
return;
}
}
c->cvec = realloc(c->cvec, ++c->len * sizeof(*c->cvec));
c->cvec[c->len - 1] = s;
}
static void CompleteFilename(const char *p, const char *q, const char *b,
linenoiseCompletions *c) {
DIR *d;
char *buf;
const char *g;
struct dirent *e;
if ((buf = malloc(512))) {
if ((g = memrchr(p, '/', q - p))) {
*(char *)mempcpy(buf, p, MIN(g - p, 511)) = 0;
p = ++g;
} else {
strcpy(buf, ".");
}
if ((d = opendir(buf))) {
while ((e = readdir(d))) {
if (!strcmp(e->d_name, ".")) continue;
if (!strcmp(e->d_name, "..")) continue;
if (!strncmp(e->d_name, p, q - p)) {
snprintf(buf, 512, "%.*s%s%s", p - b, b, e->d_name,
e->d_type == DT_DIR ? "/" : "");
AddUniqueCompletion(c, strdup(buf));
}
}
closedir(d);
}
free(buf);
}
}
static void ShellCompletion(const char *p, linenoiseCompletions *c) {
bool slashed;
const char *q, *b;
for (slashed = false, b = p, q = (p += strlen(p)); p > b; --p) {
if (p[-1] == '/' && p[-1] == '\\') slashed = true;
if (!isalnum(p[-1]) &&
(p[-1] != '.' && p[-1] != '_' && p[-1] != '-' && p[-1] != '+' &&
p[-1] != '[' && p[-1] != '/' && p[-1] != '\\')) {
break;
}
}
CompleteFilename(p, q, b, c);
}
static char *ShellHint(const char *p, const char **ansi1, const char **ansi2) {
char *h = 0;
linenoiseCompletions c = {0};
ShellCompletion(p, &c);
if (c.len == 1) {
h = strdup(c.cvec[0] + strlen(p));
}
linenoiseFreeCompletions(&c);
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;
struct rusage ru;
struct timespec ts1, ts2;
char *prog, path[PATH_MAX];
sigset_t chldmask, savemask;
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);
MakePrompt(prompt);
while ((line = linenoiseWithHistory(prompt, "cmd"))) {
n = 0;
start = line;
if (startswith(start, "time ")) {
timeit = true;
start += 5;
} 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))) {
// 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)))) {
// 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);
}
// 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);
}
// 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) {
nanos = ts2.tv_nsec - ts1.tv_nsec;
} else {
nanos = (ts2.tv_sec - ts1.tv_sec) * 1000000000LL;
nanos += 1000000000LL - ts1.tv_nsec;
nanos += ts2.tv_nsec;
}
printf("took %,ldµs wall time\n", nanos / 1000);
p = 0;
AppendResourceReport(&p, &ru, "\n");
fputs(p, stdout);
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, strsignal(WTERMSIG(ws)));
if (!__nocolor) p = stpcpy(p, "\e[0m");
*p++ = ' ';
}
MakePrompt(p);
sigprocmask(SIG_SETMASK, &savemask, 0);
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
} else {
fprintf(stderr, "%s: %s: command not found\n", argv[0], args[0]);
}
}
free(line);
free(args);
}
}

View file

@ -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));
}
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;
}

38
examples/cplusplus-stl.cc Normal file
View file

@ -0,0 +1,38 @@
/*-*-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 2020 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/stdio/stdio.h"
#include "third_party/libcxx/map"
#include "third_party/libcxx/string"
int main(int argc, char *argv[]) {
printf("std::map + std::string example\n");
std::map<std::string, int> m{
{"CPU", 10},
{"GPU", 15},
{"RAM", 20},
};
printf("m[\"CPU\"] is %d\n", m["CPU"]);
printf("m[\"RAM\"] is %d\n", m["RAM"]);
printf("m[\"GPU\"] is %d\n", m["GPU"]);
printf("setting cpu to 25\n");
m["CPU"] = 25; // update an existing value
printf("m[\"CPU\"] is %d\n", m["CPU"]);
printf("m[\"RAM\"] is %d\n", m["RAM"]);
printf("m[\"GPU\"] is %d\n", m["GPU"]);
}

View file

@ -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.
@ -22,8 +23,8 @@
* o//examples/crashreport.com
*/
int main(int argc, char *argv[]) {
noubsan int main(int argc, char *argv[]) {
volatile int64_t x;
showcrashreports();
ShowCrashReports();
return 1 / (x = 0);
}

52
examples/crashreport2.c Normal file
View file

@ -0,0 +1,52 @@
#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/log/log.h"
#include "libc/stdio/stdio.h"
/**
* @fileoverview CTRL+\ debugging example
*
* make -j8 -O o//examples/crashreport2.com
* o//examples/crashreport2.com
*
* Assuming you call ShowCrashReports() from main(), you can press
* `CTRL+\` at anny time to generate a `SIGQUIT` message that lets you
* debug wrongness and freezups.
*
* On supported platforms, this will cause GDB to automatically attach.
* The nice thing about this, is you can start stepping through your
* code at the precice instruction where the interrupt happened. See
* `libc/log/attachdebugger.c` to see how it works.
*
* If you wish to suppress the auto-GDB behavior, then:
*
* export GDB=
*
* Or alternatively:
*
* extern int __isworker;
* __isworker = true;
*
* Will cause your `SIGQUIT` handler to just print a crash report
* instead. This is useful for production software that might be running
* in a terminal environment like GNU Screen, but it's not desirable to
* have ten worker processes trying to attach GDB at once.
*/
int main(int argc, char *argv[]) {
volatile int64_t x;
ShowCrashReports();
printf("please press ctrl+\\ and see what happens...\n");
sigsuspend(0);
printf("\n\n");
printf("congratulations! your program is now resuming\n");
return 0;
}

View file

@ -36,7 +36,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "This echos stdio until Ctrl+C is pressed.\n");
CHECK_NE(-1, sigaction(SIGINT, &saint, NULL));
for (;;) {
rc = read(0, buf, BUFSIZ);
rc = read(0, buf, 512);
if (rc != -1) {
got = rc;
} else {

View file

@ -65,11 +65,26 @@
#define HeaderEqualCase(H, S) \
SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H))
int sock;
static bool TuneSocket(int fd, int a, int b, int x) {
if (!b) return false;
return setsockopt(fd, a, b, &x, sizeof(x)) != -1;
}
static void Write(const void *p, size_t n) {
ssize_t rc;
rc = write(1, p, n);
if (rc == -1 && errno == EPIPE) {
close(sock);
exit(128 + SIGPIPE);
}
if (rc != n) {
fprintf(stderr, "write failed: %m\n");
exit(1);
}
}
static int TlsSend(void *c, const unsigned char *p, size_t n) {
int rc;
NOISEF("begin send %zu", n);
@ -106,8 +121,7 @@ static wontreturn void PrintUsage(FILE *f, int rc) {
}
int main(int argc, char *argv[]) {
if (!NoDebug()) showcrashreports();
xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0);
if (!NoDebug()) ShowCrashReports();
/*
* Read flags.
@ -226,19 +240,17 @@ int main(int argc, char *argv[]) {
*/
mbedtls_ssl_config conf;
mbedtls_ssl_context ssl;
mbedtls_x509_crt *cachain = 0;
mbedtls_ctr_drbg_context drbg;
if (usessl) {
mbedtls_ssl_init(&ssl);
mbedtls_ctr_drbg_init(&drbg);
mbedtls_ssl_config_init(&conf);
cachain = GetSslRoots();
CHECK_EQ(0, mbedtls_ctr_drbg_seed(&drbg, GetEntropy, 0, "justine", 7));
CHECK_EQ(0, mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT));
mbedtls_ssl_conf_authmode(&conf, authmode);
mbedtls_ssl_conf_ca_chain(&conf, cachain, 0);
mbedtls_ssl_conf_ca_chain(&conf, GetSslRoots(), 0);
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &drbg);
if (!IsTiny()) mbedtls_ssl_conf_dbg(&conf, TlsDebug, 0);
CHECK_EQ(0, mbedtls_ssl_setup(&ssl, &conf));
@ -258,7 +270,7 @@ int main(int argc, char *argv[]) {
/*
* Connect to server.
*/
int ret, sock;
int ret;
CHECK_NE(-1, (sock = GoodSocket(addr->ai_family, addr->ai_socktype,
addr->ai_protocol, false,
&(struct timeval){-60})));
@ -283,6 +295,8 @@ int main(int argc, char *argv[]) {
CHECK_EQ(n, write(sock, request, n));
}
xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0);
/*
* Handle response.
*/
@ -330,7 +344,7 @@ int main(int argc, char *argv[]) {
break;
}
if (method == kHttpHead) {
CHECK_EQ(hdrlen, write(1, p, hdrlen));
Write(p, hdrlen);
goto Finished;
} else if (msg.status == 204 || msg.status == 304) {
goto Finished;
@ -348,32 +362,32 @@ int main(int argc, char *argv[]) {
t = kHttpClientStateBodyLengthed;
paylen = rc;
if (paylen > i - hdrlen) {
CHECK_EQ(i - hdrlen, write(1, p + hdrlen, i - hdrlen));
Write(p + hdrlen, i - hdrlen);
} else {
CHECK_EQ(paylen, write(1, p + hdrlen, paylen));
Write(p + hdrlen, paylen);
goto Finished;
}
} else {
t = kHttpClientStateBody;
CHECK_EQ(i - hdrlen, write(1, p + hdrlen, i - hdrlen));
Write(p + hdrlen, i - hdrlen);
}
}
break;
case kHttpClientStateBody:
if (!g) goto Finished;
CHECK_EQ(g, write(1, p + i - g, g));
Write(p + i - g, g);
break;
case kHttpClientStateBodyLengthed:
CHECK(g);
if (i - hdrlen > paylen) g = hdrlen + paylen - (i - g);
CHECK_EQ(g, write(1, p + i - g, g));
Write(p + i - g, g);
if (i - hdrlen >= paylen) goto Finished;
break;
case kHttpClientStateBodyChunked:
Chunked:
CHECK_NE(-1, (rc = Unchunk(&u, p + hdrlen, i - hdrlen, &paylen)));
if (rc) {
CHECK_EQ(paylen, write(1, p + hdrlen, paylen));
Write(p + hdrlen, paylen);
goto Finished;
}
break;
@ -397,7 +411,6 @@ Finished:
mbedtls_ssl_free(&ssl);
mbedtls_ctr_drbg_free(&drbg);
mbedtls_ssl_config_free(&conf);
mbedtls_x509_crt_free(cachain);
mbedtls_ctr_drbg_free(&drbg);
}

62
examples/datauri.c Normal file
View file

@ -0,0 +1,62 @@
#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/log/log.h"
#include "libc/runtime/gc.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
#include "net/http/http.h"
#include "third_party/getopt/getopt.h"
#include "third_party/stb/stb_image.h"
#define USAGE \
" [FLAGS] FILE...\n\
Utility for printing data:base64 URIs.\n\
\n\
FLAGS\n\
\n\
-h Help\n"
void PrintUsage(int rc, FILE *f) {
fputs("Usage: ", f);
fputs(program_invocation_name, f);
fputs(USAGE, f);
exit(rc);
}
void PrintUri(const char *path) {
size_t n;
void *img, *src, *mime;
int opt, i;
if (!(img = gc(xslurp(path, &n)))) exit(2);
fputs("data:", stdout);
fputs(FindContentType(path, -1), stdout);
fputs(";base64,", stdout);
fputs(gc(EncodeBase64(img, n, 0)), stdout);
}
int main(int argc, char *argv[]) {
int i;
while ((i = getopt(argc, argv, "?h")) != -1) {
switch (i) {
case '?':
case 'h':
PrintUsage(0, stdout);
default:
PrintUsage(1, stderr);
}
}
if (optind == argc) {
PrintUsage(1, stderr);
}
for (i = optind; i < argc; ++i) {
PrintUri(argv[i]);
}
}

125
examples/decompress.c Normal file
View file

@ -0,0 +1,125 @@
#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
for strategy in F L R H; do
o/$m/examples/compress.com -$strategy$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 </tmp/comp | dd count=10000 2>/tmp/info >/dev/null
decompspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info)
size=$(o/$m/examples/compress.com -$strategy$level <o/$m/examples/compress.com | wc -c)
echo "level $strategy $level $size compress $compspeed decompress $decompspeed"
done
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;
}
}

View file

@ -1,29 +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/stdio/stdio.h"
#include "libc/str/str.h"
int main(int argc, char *argv[]) {
int i, j;
bool wantnewline;
if (argc > 1 && !strcmp(argv[1], "-n")) {
i = 2;
wantnewline = false;
} else {
i = 1;
wantnewline = true;
}
for (j = 0; i + j < argc; ++j) {
if (j) fputc(' ', stdout);
fputs(argv[i + j], stdout);
}
if (wantnewline) fputc('\n', stdout);
return 0;
}

View file

@ -3,143 +3,146 @@
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))
EXAMPLES_MAINS_CC = $(filter %.cc,$(EXAMPLES_FILES))
EXAMPLES_SRCS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
EXAMPLES_SRCS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
$(EXAMPLES_MAINS_CC)
EXAMPLES_MAINS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
EXAMPLES_MAINS = \
$(EXAMPLES_MAINS_S) \
$(EXAMPLES_MAINS_C) \
$(EXAMPLES_MAINS_CC)
EXAMPLES_OBJS = \
$(EXAMPLES_MAINS_S:%.S=o/$(MODE)/%.o) \
$(EXAMPLES_MAINS_C:%.c=o/$(MODE)/%.o) \
EXAMPLES_OBJS = \
$(EXAMPLES_MAINS_S:%.S=o/$(MODE)/%.o) \
$(EXAMPLES_MAINS_C:%.c=o/$(MODE)/%.o) \
$(EXAMPLES_MAINS_CC:%.cc=o/$(MODE)/%.o)
EXAMPLES_COMS = \
$(EXAMPLES_MAINS_S:%.S=o/$(MODE)/%.com) \
$(EXAMPLES_MAINS_C:%.c=o/$(MODE)/%.com) \
EXAMPLES_COMS = \
$(EXAMPLES_MAINS_S:%.S=o/$(MODE)/%.com) \
$(EXAMPLES_MAINS_C:%.c=o/$(MODE)/%.com) \
$(EXAMPLES_MAINS_CC:%.cc=o/$(MODE)/%.com)
EXAMPLES_BINS = \
$(EXAMPLES_COMS) \
EXAMPLES_BINS = \
$(EXAMPLES_COMS) \
$(EXAMPLES_COMS:%=%.dbg)
EXAMPLES_DIRECTDEPS = \
DSP_CORE \
DSP_SCALE \
DSP_TTY \
LIBC_ALG \
LIBC_BITS \
LIBC_CALLS \
LIBC_DNS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
LIBC_NT_ADVAPI32 \
LIBC_OHMYPLUS \
LIBC_RAND \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
LIBC_THREAD \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \
LIBC_X \
LIBC_ZIPOS \
NET_HTTP \
NET_HTTPS \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LINENOISE \
THIRD_PARTY_LUA \
THIRD_PARTY_MBEDTLS \
THIRD_PARTY_MUSL \
THIRD_PARTY_QUICKJS \
THIRD_PARTY_STB \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
TOOL_BUILD_LIB \
EXAMPLES_DIRECTDEPS = \
DSP_CORE \
DSP_SCALE \
DSP_TTY \
LIBC_ALG \
LIBC_BITS \
LIBC_CALLS \
LIBC_DNS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
LIBC_NT_ADVAPI32 \
LIBC_RAND \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
LIBC_THREAD \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \
LIBC_X \
LIBC_ZIPOS \
NET_HTTP \
NET_HTTPS \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LIBCXX \
THIRD_PARTY_LINENOISE \
THIRD_PARTY_LUA \
THIRD_PARTY_MBEDTLS \
THIRD_PARTY_MUSL \
THIRD_PARTY_QUICKJS \
THIRD_PARTY_STB \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
TOOL_BUILD_LIB \
TOOL_VIZ_LIB
EXAMPLES_DEPS := \
EXAMPLES_DEPS := \
$(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x))))
o/$(MODE)/examples/examples.pkg: \
$(EXAMPLES_OBJS) \
o/$(MODE)/examples/examples.pkg: \
$(EXAMPLES_OBJS) \
$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/examples/unbourne.o: \
OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
o/$(MODE)/examples/unbourne.o: \
OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED \
-fpie
o/$(MODE)/examples/%.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/%.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE)
o/$(MODE)/examples/%.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/%.o \
o/$(MODE)/examples/examples.pkg \
$(EXAMPLES_BOOTLOADER)
@$(APELINK)
o/$(MODE)/examples/hellolua.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/hellolua.o \
o/$(MODE)/examples/hellolua.lua.zip.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/examples/ispell.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/ispell.o \
o/$(MODE)/usr/share/dict/words.zip.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/examples/nesemu1.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/nesemu1.o \
o/$(MODE)/usr/share/rom/mario.nes.zip.o \
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)
@$(APELINK)
o/$(MODE)/examples/hello.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/hello.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
o/$(MODE)/examples/nomodifyself.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/nomodifyself.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/examples/hellolua.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/hellolua.o \
o/$(MODE)/examples/hellolua.lua.zip.o \
o/$(MODE)/examples/examples.pkg \
$(EXAMPLES_BOOTLOADER)
@$(APELINK)
o/$(MODE)/examples/ispell.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/ispell.o \
o/$(MODE)/usr/share/dict/words.zip.o \
o/$(MODE)/examples/examples.pkg \
$(EXAMPLES_BOOTLOADER)
@$(APELINK)
o/$(MODE)/examples/nesemu1.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/nesemu1.o \
o/$(MODE)/usr/share/rom/mario.nes.zip.o \
o/$(MODE)/usr/share/rom/zelda.nes.zip.o \
o/$(MODE)/usr/share/rom/tetris.nes.zip.o \
o/$(MODE)/examples/examples.pkg \
$(EXAMPLES_BOOTLOADER)
@$(APELINK)
o/$(MODE)/examples/picol.o: \
OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
@ -152,15 +155,32 @@ o/$(MODE)/examples/picol.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/examples/nesemu1.com: \
o/$(MODE)/examples/nesemu1.com.dbg \
o/$(MODE)/third_party/zip/zip.com \
o/$(MODE)/tool/build/symtab.com
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/examples/.nesemu1/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/examples/.nesemu1/.symtab
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: \
o/$(MODE)/examples/package \
o/$(MODE)/examples/pylife \
o/$(MODE)/examples/pyapp \
$(EXAMPLES_BINS)

View file

@ -8,14 +8,25 @@
*/
#endif
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/sig.h"
STATIC_YOINK("strerror");
int main(int argc, char *argv[]) {
if (argc < 3) {
fputs("USAGE: EXEC.COM PROG ARGV₀ [ARGV₁...]\n", stderr);
sigset_t ss;
if (argc < 2) {
fputs("USAGE: EXEC.COM PROG [ARGV₀...]\n", stderr);
return 1;
}
// block arbitrary signal so __printargs() looks cooler
sigemptyset(&ss);
sigaddset(&ss, SIGPWR);
sigprocmask(SIG_BLOCK, &ss, 0);
execv(argv[1], argv + 2);
return 127;
}

14
examples/exit.c Normal file
View file

@ -0,0 +1,14 @@
#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/fmt/conv.h"
int main(int argc, char *argv[]) {
return atoi(argc > 1 ? argv[1] : "0");
}

23
examples/forkexec.c Normal file
View file

@ -0,0 +1,23 @@
#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/stdio/stdio.h"
int main(int argc, char *argv[]) {
int pid;
if (argc < 3) {
fputs("USAGE: FORKEXEC.COM PROG ARGV₀ [ARGV₁...]\n", stderr);
return 1;
}
if (!fork()) {
execv(argv[1], argv + 2);
return 127;
}
}

25
examples/forkexecwait.c Normal file
View file

@ -0,0 +1,25 @@
#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/stdio/stdio.h"
int main(int argc, char *argv[]) {
int pid;
volatile void *p;
if (argc < 3) {
fputs("USAGE: FORKEXECWAIT.COM PROG ARGV₀ [ARGV₁...]\n", stderr);
return 1;
}
if (!fork()) {
execv(argv[1], argv + 2);
return 127;
}
wait(0);
}

View file

@ -8,13 +8,17 @@
*/
#endif
#include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/nt/nt/process.h"
#include "libc/rand/rand.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
dontinline void dostuff(const char *s) {
int i, us;

View file

@ -14,11 +14,11 @@
int main(int argc, char *argv[]) {
int c, n;
char a[22];
char a[22], *p;
if ((c = GetCpuCount())) {
n = int64toarray_radix10(c, a);
a[n++] = '\n';
return write(1, a, n) == n ? 0 : 1;
p = FormatInt64(a, c);
*p++ = '\n';
return write(1, a, p - a) == p - a ? 0 : 1;
} else {
return 1;
}

View file

@ -209,6 +209,7 @@ const struct Function {
{"getrandom", GetRandom}, //
{"inc", inc}, //
{"knuth", knuth}, //
{"lemur64", lemur64}, //
{"libc", libc}, //
{"moby", moby}, //
{"mt19937", _mt19937}, //

351
examples/greenbean.c Normal file
View file

@ -0,0 +1,351 @@
#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/struct/sigaction.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/intrin/wait0.internal.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/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"
#include "libc/sysv/consts/clock.h"
#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"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/sysv/consts/tcp.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "net/http/http.h"
#include "net/http/url.h"
/**
* @fileoverview greenbean lightweighht threaded web server
*
* $ make -j8 o//tool/net/greenbean.com
* $ o//tool/net/greenbean.com &
* $ printf 'GET /\n\n' | nc 127.0.0.1 8080
* HTTP/1.1 200 OK
* Server: greenbean/1.o
* Referrer-Policy: origin
* Cache-Control: private; max-age=0
* Content-Type: text/html; charset=utf-8
* Date: Sat, 14 May 2022 14:13:07 GMT
* Content-Length: 118
*
* <!doctype html>
* <title>hello world</title>
* <h1>hello world</h1>
* <p>this is a fun webpage
* <p>hosted by greenbean
*
* Like redbean, greenbean has superior performance too, with an
* advantage on benchmarks biased towards high connection counts
*
* $ wrk -c 300 -t 32 --latency http://127.0.0.1:8080/
* Running 10s test @ http://127.0.0.1:8080/
* 32 threads and 300 connections
* Thread Stats Avg Stdev Max +/- Stdev
* Latency 661.06us 5.11ms 96.22ms 98.85%
* Req/Sec 42.38k 8.90k 90.47k 84.65%
* Latency Distribution
* 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 PORT 8080
#define HEARTBEAT 100
#define KEEPALIVE 5000
#define LOGGING 0
#define STANDARD_RESPONSE_HEADERS \
"Server: greenbean/1.o\r\n" \
"Referrer-Policy: origin\r\n" \
"Cache-Control: private; max-age=0\r\n"
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, 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(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));
errno = 0;
if (bind(server, &addr, sizeof(addr)) == -1) {
kprintf("%s() failed %m\n", "socket");
goto CloseWorker;
}
listen(server, 1);
// connection loop
++listening;
while (!closingtime) {
struct tm tm;
int64_t unixts;
struct Url url;
ssize_t got, sent;
struct timespec ts;
struct HttpMessage msg;
uint32_t clientaddrsize;
struct sockaddr_in clientaddr;
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);
// accept() can raise a very diverse number of errors but none of
// them are really true showstoppers that would necessitate us to
// panic and abort the entire server, so we can just ignore these
if (client == -1) {
// we used SO_RCVTIMEO and SO_SNDTIMEO because those settings are
// 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 shutdowne event; otherwise, we retry the accept call
continue;
}
++connections;
// message loop
do {
// parse the incoming http message
InitHttpMessage(&msg, kHttpRequest);
// we're not terrible concerned when errors happen here
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("%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,
inbuf + msg.uri.a);
#endif
// display hello world html page for http://127.0.0.1:8080/
if (msg.method == kHttpGet &&
(msg.uri.b - msg.uri.a == 1 && inbuf[msg.uri.a + 0] == '/')) {
q = "<!doctype html>\r\n"
"<title>hello world</title>\r\n"
"<h1>hello world</h1>\r\n"
"<p>this is a fun webpage\r\n"
"<p>hosted by greenbean\r\n";
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(0, &ts), unixts = ts.tv_sec;
p = FormatHttpDateTime(p, gmtime_r(&unixts, &tm));
p = stpcpy(p, "\r\nContent-Length: ");
p = FormatInt32(p, strlen(q));
p = stpcpy(p, "\r\n\r\n");
p = stpcpy(p, q);
outmsglen = p - outbuf;
sent = write(client, outbuf, outmsglen);
} else {
// display 404 not found error page for every thing else
q = "<!doctype html>\r\n"
"<title>404 not found</title>\r\n"
"<h1>404 not found</h1>\r\n";
p = stpcpy(outbuf,
"HTTP/1.1 404 Not Found\r\n" STANDARD_RESPONSE_HEADERS
"Content-Type: text/html; charset=utf-8\r\n"
"Date: ");
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));
p = stpcpy(p, "\r\n\r\n");
p = stpcpy(p, q);
outmsglen = p - outbuf;
sent = write(client, outbuf, p - outbuf);
}
// if the client isn't pipelining and write() wrote the full
// amount, then since we sent the content length and checked
// that the client didn't attach a payload, we are so synced
// thus we can safely process more messages
} while (got == inmsglen && sent == outmsglen &&
!msg.headers[kHttpContentLength].a &&
!msg.headers[kHttpTransferEncoding].a &&
(msg.method == kHttpGet || msg.method == kHttpHead));
DestroyHttpMessage(&msg);
close(client);
--connections;
}
--listening;
// inform the parent that this clone has finished
CloseWorker:
close(server);
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[]) {
int i;
char **tls;
char **stack;
uint32_t *hostips;
// ShowCrashReports();
// 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);
// 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);
}
// 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");
}
// wait for workers to terminate
while (workers) {
PrintStatus();
usleep(HEARTBEAT * 1000);
}
// clean up terminal line
kprintf("\r\e[K");
// join the workers
// this is how we guarantee stacks are safe to free
if (tls && stack) {
for (i = 0; i < threads; ++i) {
_wait0((int *)(tls[i] + 0x38));
munmap(stack[i], GetStackSize());
free(tls[i]);
}
}
// clean up memory
free(hostips);
free(stack);
free(tls);
}

View file

@ -44,8 +44,8 @@
#include "libc/str/str.h"
#include "libc/time/time.h"
#include "third_party/zlib/zlib.h"
// clang-format off
/* clang-format off */
#define DICT "usr/share/dict/hangman"
#define MAXERR 7
#define MINSCORE 0

View file

@ -9,6 +9,8 @@
#endif
#include "libc/stdio/stdio.h"
STATIC_YOINK("mmap"); // TODO: fix bandaid for MODE=asan
int main() {
printf("%s\n", "hello world");
return 0;

18
examples/hello4.c Normal file
View file

@ -0,0 +1,18 @@
#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/math.h"
#include "libc/stdio/stdio.h"
int main(int argc, char *argv[]) {
volatile long double x = -.5;
volatile long double y = 1.5;
printf("atan2l(%.19Lg, %.19Lg) is %.19Lg\n", x, y, atan2l(x, y));
return 0;
}

View file

@ -7,23 +7,98 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/dce.h"
#include "libc/log/log.h"
#include "libc/runtime/gc.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
#include "net/http/http.h"
#include "third_party/getopt/getopt.h"
#include "third_party/stb/stb_image.h"
/**
* @fileoverview Utility for printing HTML <img> tags.
*/
#define USAGE \
" [FLAGS] IMG...\n\
Utility for printing HTML <img> tags.\n\
\n\
FLAGS\n\
\n\
-h Help\n\
-a Wrap with <a> tag\n\
-u Embed data:base64 URI\n"
int scale;
bool linktag;
bool datauri;
bool sizeonly;
void PrintUsage(int rc, FILE *f) {
fputs("Usage: ", f);
fputs(program_invocation_name, f);
fputs(USAGE, f);
exit(rc);
}
void PrintImg(const char *path) {
size_t n;
int opt, i, yn, xn, cn, w, h;
void *img, *pix, *src, *mime;
if (!(img = gc(xslurp(path, &n)))) exit(2);
if (!(pix = gc(stbi_load_from_memory(img, n, &xn, &yn, &cn, 0)))) exit(3);
if (linktag) {
printf("<a href=\"%s\"\n >", path);
}
src = path;
if (datauri) {
src = xasprintf("data:%s;base64,%s", FindContentType(path, -1),
gc(EncodeBase64(img, n, &n)));
}
w = (xn + (1 << scale) / 2) >> scale;
h = (yn + (1 << scale) / 2) >> scale;
if (sizeonly) {
printf("width=\"%d\" height=\"%d\"", w, h);
} else {
printf("<img width=\"%d\" height=\"%d\" alt=\"[%s]\"\n src=\"%s\">", w,
h, path, src);
}
if (linktag) {
printf("</a>");
}
printf("\n");
}
int main(int argc, char *argv[]) {
void *p;
size_t n;
int yn, xn, cn;
if (argc != 2) return 1;
if (!(p = xslurp(argv[1], &n))) return 2;
if (!(p = stbi_load_from_memory(p, n, &xn, &yn, &cn, 0))) return 3;
printf("<img src=\"%s\" width=\"%d\" height=\"%d\"\n"
" alt=\"[%s]\">\n",
argv[1], (xn + 1) >> 1, (yn + 1) >> 1, argv[1]);
return 0;
if (!NoDebug()) ShowCrashReports();
int i;
while ((i = getopt(argc, argv, "?huas01234")) != -1) {
switch (i) {
case '0':
case '1':
case '2':
case '3':
case '4':
scale = i - '0';
break;
case 's':
sizeonly = true;
break;
case 'a':
linktag = true;
break;
case 'u':
datauri = true;
break;
case '?':
case 'h':
PrintUsage(0, stdout);
default:
PrintUsage(1, stderr);
}
}
if (optind == argc) {
PrintUsage(1, stderr);
}
for (i = optind; i < argc; ++i) {
PrintImg(argv[i]);
}
}

View file

@ -121,7 +121,7 @@ void SpellChecker(void) {
printf("word: ");
fflush(stdout);
if (getline(&line, &linesize, stdin) > 0) {
query = strtolower(chomp(line));
query = strtolower(_chomp(line));
if (critbit0_contains(&words, query)) {
printf("ok\r\n");
} else {
@ -147,13 +147,13 @@ void SpellChecker(void) {
void LoadWords(void) {
CHECK_NOTNULL((f = fopen("/zip/usr/share/dict/words", "r")));
while (getline(&line, &linesize, f) > 0) {
critbit0_insert(&words, strtolower(chomp(line)));
critbit0_insert(&words, strtolower(_chomp(line)));
}
CHECK_NE(-1, fclose(f));
}
int main(int argc, char *argv[]) {
showcrashreports();
if (!NoDebug()) ShowCrashReports();
LoadWords();
SpellChecker();
return 0;

View file

@ -154,7 +154,7 @@ void editorSetStatusMessage(const char *fmt, ...);
* matches and keywords. The file name matches are used in order to match
* a given syntax with a given file name: if a match pattern starts with a
* dot, it is matched as the last past of the filename, for example ".c".
* Otherwise the pattern is just searched inside the filenme, like "Makefile").
* Otherwise the pattern is just searched inside the filename, like "Makefile").
*
* The list of keywords to highlight is just a list of words, however if they
* a trailing '|' character is added at the end, they are highlighted in
@ -249,7 +249,10 @@ fatal:
int editorReadKey(int64_t fd) {
int nread;
char c, seq[3];
if ((nread = read(fd, &c, 1)) == -1) exit(1);
do {
nread = read(fd, &c, 1);
if (nread == -1) exit(1);
} while (!nread);
while (1) {
switch (c) {
@ -390,7 +393,7 @@ int is_separator(int c) {
/* Return true if the specified row last char is part of a multi line comment
* that starts at this row or at one before, and does not end at the end
* of the row but spawns to the next row. */
* of the row but spans to the next row. */
int editorRowHasOpenComment(erow *row) {
if (row->hl && row->rsize && row->hl[row->rsize - 1] == HL_MLCOMMENT &&
(row->rsize < 2 || (row->render[row->rsize - 2] != '*' ||
@ -651,7 +654,7 @@ void editorFreeRow(erow *row) {
free(row->hl);
}
/* Remove the row at the specified position, shifting the remainign on the
/* Remove the row at the specified position, shifting the remaining on the
* top. */
void editorDelRow(int at) {
erow *row;
@ -667,7 +670,7 @@ void editorDelRow(int at) {
/* Turn the editor rows into a single heap-allocated string.
* Returns the pointer to the heap-allocated string and populate the
* integer pointed by 'buflen' with the size of the string, escluding
* integer pointed by 'buflen' with the size of the string, excluding
* the final nulterm. */
char *editorRowsToString(int *buflen) {
char *buf = NULL, *p;

23
examples/linenoise.c Normal file
View file

@ -0,0 +1,23 @@
#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/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "third_party/linenoise/linenoise.h"
int main(int argc, char *argv[]) {
char *line;
while ((line = linenoiseWithHistory("IN> ", "foo"))) {
fputs("OUT> ", stdout);
fputs(line, stdout);
fputs("\n", stdout);
free(line);
}
return 0;
}

20
examples/loadavg.c Normal file
View file

@ -0,0 +1,20 @@
#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/log/check.h"
#include "libc/stdio/stdio.h"
#include "libc/time/time.h"
int main(int argc, char *argv[]) {
double x[3];
CHECK_NE(-1, getloadavg(x, 3));
printf("%g %g %g\n", x[0], x[1], x[2]);
return 0;
}

View file

@ -15,6 +15,7 @@
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/sysv/consts/s.h"
#include "libc/x/x.h"
struct stat st;

17
examples/mkhello.c Normal file
View file

@ -0,0 +1,17 @@
#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"
int main(int argc, char *argv[]) {
creat("hello.txt", 0644);
write(3, "hello\n", 6);
close(3);
return 0;
}

View file

@ -26,7 +26,6 @@
#include "libc/macros.internal.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/ohmyplus/vector.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
@ -43,6 +42,7 @@
#include "libc/zip.h"
#include "libc/zipos/zipos.internal.h"
#include "third_party/getopt/getopt.h"
#include "third_party/libcxx/vector"
#include "tool/viz/lib/knobs.h"
STATIC_YOINK("zip_uri_support");
@ -1673,7 +1673,7 @@ char* GetLine(void) {
static char* line;
static size_t linesize;
if (getline(&line, &linesize, stdin) > 0) {
return chomp(line);
return _chomp(line);
} else {
return NULL;
}
@ -1703,7 +1703,7 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
if ((ffplay = commandvenv("FFPLAY", "ffplay"))) {
devnull = open("/dev/null", O_WRONLY | O_CLOEXEC);
pipe2(pipefds, O_CLOEXEC);
if (!(playpid_ = vfork())) {
if (!(playpid_ = fork())) {
const char* const args[] = {
"ffplay", "-nodisp", "-loglevel", "quiet", "-fflags",
"nobuffer", "-ac", "1", "-ar", "1789773",

36
examples/nomodifyself.c Normal file
View file

@ -0,0 +1,36 @@
#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/dce.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.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[]) {
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 {
printf("error: %s doesn't have an MZ file header!\n", argv[0]);
return 1;
}
}

View file

@ -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.

View file

@ -12,6 +12,7 @@
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/struct/winsize.h"
#include "libc/dce.h"
#include "libc/log/check.h"
#include "libc/log/gdb.h"
#include "libc/log/log.h"
@ -152,7 +153,7 @@ void Draw(void) {
int main(int argc, char *argv[]) {
struct sigaction sa[2] = {{.sa_handler = OnShutdown},
{.sa_handler = OnInvalidate}};
showcrashreports();
if (!NoDebug()) ShowCrashReports();
Setup();
Enter();
GetTtySize();

View file

@ -7,94 +7,8 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/nt/process.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h"
const struct AuxiliaryValue {
const char *fmt;
long *id;
const char *name;
const char *description;
} kAuxiliaryValues[] = {
{"%012lx", &AT_EXECFD, "AT_EXECFD", "file descriptor of program"},
{"%012lx", &AT_PHDR, "AT_PHDR", "address of elf program headers"},
{"%012lx", &AT_PHENT, "AT_PHENT", "size of program header entry"},
{"%012lx", &AT_PHNUM, "AT_PHNUM", "number of program headers"},
{"%012lx", &AT_PAGESZ, "AT_PAGESZ", "system page size"},
{"%012lx", &AT_BASE, "AT_BASE", "base address of the program interpreter"},
{"%012lx", &AT_ENTRY, "AT_ENTRY", "entry address of executable"},
{"%012lx", &AT_NOTELF, "AT_NOTELF", "set if not an elf"},
{"%-12d", &AT_UID, "AT_UID", "real user id of thread"},
{"%-12d", &AT_EUID, "AT_EUID", "effective user id of thread"},
{"%-12d", &AT_GID, "AT_GID", "real group id of thread"},
{"%-12d", &AT_EGID, "AT_EGID", "effective group id of thread"},
{"%-12d", &AT_CLKTCK, "AT_CLKTCK", "frequency of times() counts"},
{"%012lx", &AT_OSRELDATE, "AT_OSRELDATE",
"freebsd release number, e.g. 1200086"},
{"%012lx", &AT_PLATFORM, "AT_PLATFORM",
"string identifying hardware platform"},
{"%012lx", &AT_DCACHEBSIZE, "AT_DCACHEBSIZE", "data cache block size"},
{"%012lx", &AT_ICACHEBSIZE, "AT_ICACHEBSIZE",
"instruction cache block size"},
{"%012lx", &AT_UCACHEBSIZE, "AT_UCACHEBSIZE", "unified cache block size"},
{"%012lx", &AT_SECURE, "AT_SECURE",
"for set{u,g}id binz & security blankets"},
{"%-12s", &AT_BASE_PLATFORM, "AT_BASE_PLATFORM",
"string identifying real platform"},
{"%012lx", &AT_RANDOM, "AT_RANDOM", "address of sixteen random bytes"},
{"%-12s", &AT_EXECFN, "AT_EXECFN", "pathname used to execute program"},
{"%012lx", &AT_SYSINFO_EHDR, "AT_SYSINFO_EHDR",
"linux virtual dso page address"},
{"%012lx", &AT_FLAGS, "AT_FLAGS", "unused?"},
{"%012lx", &AT_HWCAP, "AT_HWCAP", "cpu stuff"},
{"%012lx", &AT_HWCAP2, "AT_HWCAP2", "more cpu stuff"},
};
const struct AuxiliaryValue *DescribeAuxv(unsigned long x) {
int i;
for (i = 0; i < ARRAYLEN(kAuxiliaryValues); ++i) {
if (x == *kAuxiliaryValues[i].id) {
return kAuxiliaryValues + i;
}
}
return NULL;
}
int main(int argc, char *argv[], char **envp) {
long key;
unsigned i;
unsigned long *auxp;
char fmt[64], **env;
struct AuxiliaryValue *auxinfo;
uint32_t varlen;
char16_t var[PATH_MAX];
printf("\nArguments:\n");
for (i = 0; i < __argc; ++i) {
printf(" ☼ %s\n", argv[i]);
}
printf("\nEnvironment:\n");
for (env = envp; *env; ++env) {
printf(" ☼ %s\n", *env);
}
printf("\nAuxiliary Values:\n");
for (auxp = __auxv; *auxp; auxp += 2) {
if ((auxinfo = DescribeAuxv(auxp[0]))) {
stpcpy(stpcpy(stpcpy(fmt, "%16s[%4ld] = "), auxinfo->fmt), " # %s\n");
printf(fmt, auxinfo->name, auxp[0], auxp[1], auxinfo->description);
} else {
printf("%16s[%4ld] = %012lx\n", "unknown", auxp[0], auxp[1]);
}
}
printf("\nSpecial Directories:\n");
printf(" ☼ kTmpPath = %`'s\n", kTmpPath);
printf(" ☼ kNtSystemDirectory = %`'s\n", kNtSystemDirectory);
printf(" ☼ kNtWindowsDirectory = %`'s\n", kNtWindowsDirectory);
printf(" ☼ program_executable_name = %`'s\n", program_executable_name);
return 0;
int main() {
__printargs("");
}

View file

@ -27,7 +27,7 @@ int main(int argc, char *argv[]) {
}
}
if (isprime) {
int64toarray_radix10(i, buf);
FormatInt64(buf, i);
fputs(buf, stdout);
fputc('\n', stdout);
if (k++ % 100 == 0) {

View file

@ -95,8 +95,8 @@ o/$(MODE)/examples/pyapp/pyapp.com.dbg: \
o/$(MODE)/examples/pyapp/pyapp.pkg \
o/$(MODE)/examples/pyapp/pyapp.o \
$(CRT) \
$(APE)
$(LINK) $(LINKARGS) -o $@
$(APE_NO_MODIFY_SELF)
@$(COMPILE) -ALINK.ape $(LINK) $(LINKARGS) -o $@
# # Unwrap the APE .COM binary, that's embedded within the linked file
# # NOTE: This line can be commented out, since it's in build/rules.mk

116
examples/pylife/pylife.mk Normal file
View file

@ -0,0 +1,116 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
#
# SYNOPSIS
#
# Actually Portable Python Tutorial
#
# DESCRIPTION
#
# This tutorial demonstrates how to compile Python apps as tiny
# static multiplatform APE executables as small as 1.9m in size
# using Cosmopolitan, which is a BSD-style multitenant codebase
#
# GETTING STARTED
#
# # run these commands after cloning the cosmo repo on linux
# $ make -j8 o//examples/pylife/pylife.com
# $ o//examples/pylife/pylife.com
# cosmopolitan is cool!
#
# HOW IT WORKS
#
# $ pyobj.com -m -o pylife.o pylife.py
# $ ld -static -nostdlib -T o//ape/ape.lds ape.o crt.o \
# pylife.o \
# cosmopolitan-python-stage2.a \
# cosmopolitan-sqlite3.a \
# cosmopolitan-linenoise.a \
# cosmopolitan-bzip2.a \
# cosmopolitan-python-stage1.a \
# cosmopolitan.a
# $ ./pylife.com
# cosmopolitan is cool!
#
# NOTES
#
# If you enjoy this tutorial, let us know jtunney@gmail.com. If
# you're building something cool, then we can we can add you to
# our .gitowners file which grants you commit access so you can
# indepnedently maintain your package, as part of the mono-repo
PKGS += PYLIFE
PYLIFE = $(PYLIFE_DEPS) o/$(MODE)/examples/pylife/pylife.a
PYLIFE_COMS = o/$(MODE)/examples/pylife/pylife.com
PYLIFE_BINS = $(PYLIFE_COMS) $(PYLIFE_COMS:%=%.dbg)
# Specify our Cosmopolitan library dependencies
#
# - THIRD_PARTY_PYTHON_STAGE1 plus THIRD_PARTY_PYTHON_STAGE2 will
# define the Python CAPI and supported standard library modules
#
PYLIFE_DIRECTDEPS = \
THIRD_PARTY_PYTHON_STAGE2
# Compute the transitive closure of dependencies. There's dozens of
# other static libraries we need, in order to build a static binary
# such as fmt.a, runtime.a, str.a etc. This magic statement figures
# them all out and arranges them in the correct order.
PYLIFE_DEPS := $(call uniq,$(foreach x,$(PYLIFE_DIRECTDEPS),$($(x))))
# # Asks PYOBJ.COM to turn our Python source into an ELF object which
# # contains (a) embedded zip file artifacts of our .py file and .pyc
# # which it it compiled; and (b) statically analyzed listings of our
# # python namespaces and imports that GNU ld will use for tree shake
# # NOTE: This code can be commented out since it's in build/rules.mk
# o/$(MODE)/examples/pylife/pylife.o: examples/pylife/pylife.py o/$(MODE)/third_party/python/pyobj
# o/$(MODE)/third_party/python/pyobj $(PYFLAGS) -o $@ $<
# We need to define how the repository source code path gets mapped
# into an APE ZIP file path. By convention, we place Python modules
# in `.python/` (which is accessible via open() system calls, using
# the synthetic path `"/zip/.python/"`) which means that if we want
# to turn `pylife/pylife.py` into `.python/pylife.py` so it's imported
# using `import pylife` then we can simply append to PYOBJ.COM flags
# flags above asking it to strip one directory component and prefix
# Lastly be sure that whenever you use this variable override trick
# you only do it to .o files, since otherwise it'll ruin everything
# Passing -m to PYOBJ.COM causes a C main() function to get yoinked
# and it means our Python module can no longer be used as a library
o/$(MODE)/examples/pylife/pylife.o: PYFLAGS += -m -C2 -P.python
# Asks PACKAGE.COM to sanity check our DIRECTDEPS and symbol graph.
# This program functions as an incremental linker. It also provides
# enhancements to the object code that GCC generated similar to LTO
# so be certain that your .com.dbg rule depends on the .pkg output!
o/$(MODE)/examples/pylife/pylife.pkg: \
o/$(MODE)/examples/pylife/pylife.o \
$(foreach x,$(PYLIFE_DIRECTDEPS),$($(x)_A).pkg)
# Ask GNU LD to link our APE executable within an ELF binary shell.
# The CRT and APE dependencies are special dependencies that define
# your _start() / WinMain() entrpoints as well as APE linker script
o/$(MODE)/examples/pylife/pylife.com.dbg: \
$(PYLIFE_DEPS) \
o/$(MODE)/examples/pylife/pylife.pkg \
o/$(MODE)/examples/pylife/pylife.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
$(LINK) $(LINKARGS) -o $@
# # Unwrap the APE .COM binary, that's embedded within the linked file
# # NOTE: This line can be commented out, since it's in build/rules.mk
# o/$(MODE)/examples/pylife/pylife.com: \
# o/$(MODE)/examples/pylife/pylife.com.dbg
# $(OBJCOPY) -S -O binary $< $@
# Ensure that build config changes will invalidate build artifacts.
o/$(MODE)/examples/pylife/pylife.o: \
examples/pylife/pylife.mk
# By convention we want to be able to say `make -j8 o//examples/pylife`
# and have it build all targets the package defines.
.PHONY: o/$(MODE)/examples/pylife
o/$(MODE)/examples/pylife: \
o/$(MODE)/examples/pylife/pylife.com \
o/$(MODE)/examples/pylife/pylife.com.dbg

View file

@ -0,0 +1,2 @@
import sys
sys.exit(42)

Some files were not shown because too many files have changed in this diff Show more