cosmopolitan/bin/cosmocc
Justine Tunney f531acc8f9
Make improvements
- Invent openatemp() API
- Invent O_UNLINK open flag
- Introduce getenv_secure() API
- Remove `git pull` from cosmocc
- Fix utimes() when path is NULL
- Fix mktemp() to never return NULL
- Fix utimensat() UTIME_OMIT on XNU
- Improve utimensat() code for RHEL5
- Turn `argv[0]` C:/ to /C/ on Windows
- Introduce tmpnam() and tmpnam_r() APIs
- Fix more const issues with internal APIs
- Permit utimes() on WIN32 in O_RDONLY mode
- Fix fdopendir() to check fd is a directory
- Fix recent crash regression in landlock make
- Fix futimens(AT_FDCWD, NULL) to return EBADF
- Use workaround so `make -j` doesn't fork bomb
- Rename dontdiscard to __wur (just like glibc)
- Fix st_size for WIN32 symlinks containing UTF-8
- Introduce stdio ext APIs needed by GNU coreutils
- Fix lstat() on WIN32 for symlinks to directories
- Move some constants from normalize.inc to limits.h
- Fix segv with memchr() and memcmp() overlapping page
- Implement POSIX fflush() behavior for reader streams
- Implement AT_SYMLINK_NOFOLLOW for utimensat() on WIN32
- Don't change read-only status of existing files on WIN32
- Correctly handle `0x[^[:xdigit:]]` case in strtol() functions
2023-09-06 12:34:59 -07:00

375 lines
11 KiB
Bash
Executable file

#!/bin/sh
#
# cosmopolitan c/c++ compiler
#
# getting started
#
# sudo chmod 1777 /opt # sticky bit isn't required
# git clone https://github.com/jart/cosmopolitan /opt/cosmo
# export PATH="/opt/cosmo/bin:/opt/cosmos/bin:$PATH"
# echo 'export PATH="/opt/cosmo/bin:/opt/cosmos/bin:$PATH"' >>~/.profile
# ape-install # optionally install a faster systemwide ape loader
# cosmocc --update # pull and rebuild toolchain artifacts
#
# getting started synopsis
#
# cosmocc -o hello.com hello.c
# ./foo.com
# ./foo.com --strace
# ./foo.com --ftrace
#
# how to build a project like lua 5.4.6
#
# make all test CC=cosmocc
# src/lua -e 'print("hi")'
# make install INSTALL_TOP=/opt/cosmos
# apecopy src/lua src/lua.com # convert to portable ape binary
#
# how to cross compile a project like lua 5.4.6
#
# aarch64-unknown-cosmo-cc --update
# make clean all test CC=aarch64-unknown-cosmo-cc AR='aarch64-unknown-cosmo-ar rc' -j8
# qemu-aarch64 src/lua -e 'print("hi")'
# make install INSTALL_TOP=/opt/cosmos/aarch64
#
# building in tiny mode
#
# export MODE=tiny
# cosmocc --update
# cosmocc -Os -o foo.com foo.c
#
# building in tiniest mode (linux only)
#
# export MODE=tinylinux
# cosmocc --update
# cosmocc -Os -o foo.com foo.c
#
# hardening programs with memory safety
#
# export MODE=asan
# cosmocc --update
# cosmocc -o foo.com foo.c
#
# detecting this environment
#
# - `__COSMOCC__` is defined when this compiler is in play
# - `__COSMOPOLITAN__` is always defined by Cosmopolitan Libc
#
# some notes on this compiler
#
# - the underlying compiler itself is gcc
# - we use cosmopoiltan libc rather than glibc
# - we use llvm's compiler-rt and libcxx runtimes
# - we patched gcc so switch case can have symbols
# - our scanf() implementation is somewhat troubled
#
# compiler flags that aren't supported
#
# - `-r` partial linking not implemented yet (todo)
# - `-S` you need to put your assembly behind #ifdefs instead
# - `-fexceptions` cosmopolitan doesn't support c++ exceptions yet
# - `-frtti` cosmopolitan doesn't support c++ runtime reflection yet
# - `-mred-zone` the system v red zone doesn't exist on windows and metal
# - `-fpic`, '-fPIC', `-shared`, `-pie`, etc. no shared object support yet
# - `-fsanitize=thread` cosmopolitan doesn't have thread sanitizer runtime yet
# - `-fomit-frame-pointer` is partially supported (apple forbids full removal)
#
# for further details, run `man gcc`
PROG=${0##*/}
COSMO=${COSMO:-/opt/cosmo}
GCC_VERSION=11.2.0
if [ "$1" = "--version" ]; then
cat <<EOF
$PROG (GCC) $GCC_VERSION
Copyright (c) 2023 Justine Alexandra Roberts Tunney
Cosmopolitan Libc and LLVM libcxx/compiler-rt are subject to non-GPL
notice licenses, e.g. ISC, MIT, etc. Your compiled programs must embed
our copyright notices. This toolchain is configured to do so default.
Cosmopolitan comes with absolutely NO WARRANTY of any kind.
For more information, see the Cosmopolitan LICENSE files.
Copyright (C) 2019 Free Software Foundation, Inc.
This launches GNU GCC/Binutils subprocesses, which is free software; see
Cosmopolitan's third_party/gcc/ for source code and copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
EOF
exit
fi
fatal_error() {
echo "$PROG: fatal error: $1" >&2
echo "compilation terminated." >&2
exit 1
}
log_command() {
if [ -n "$BUILDLOG" ]; then
printf '# %s\n(cd %s; %s)\n' "$ORIGINAL" "$PWD" "$*" >>"$BUILDLOG"
fi
}
ORIGINAL="$0 $*"
PLATFORM="-D__COSMOPOLITAN__ -D__COSMOCC__"
PREDEF="-include libc/integral/normalize.inc"
CFLAGS="-fportcosmo -fno-dwarf2-cfi-asm -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-semantic-interposition"
CPPFLAGS="-fno-pie -nostdinc -fno-math-errno -iquote $COSMO"
LDFLAGS="-static -no-pie -nostdlib -fuse-ld=bfd"
APEFLAGS="-Wl,--gc-sections"
PRECIOUS="-fno-omit-frame-pointer"
CROSS=1
ARCH=${PROG%%-*} # split(prog, '-')[0]
if [ x"$ARCH" = x"$PROG" ]; then
ARCH=x86_64
CROSS=0
fi
if [ x"$ARCH" = x"x86_64" ]; then
MODE=${MODE:-$m}
else
MODE=${MODE:-${m:-$ARCH}}
fi
if [ $CROSS -eq 0 ]; then
COSMOS=${COSMOS:-/opt/cosmos}
else
COSMOS=${COSMOS:-/opt/cosmos/$ARCH}
fi
CC="$COSMO/o/third_party/gcc/bin/$ARCH-linux-musl-gcc"
CRT="$COSMO/o/$MODE/libc/crt/crt.o"
LDLIBS="$COSMO/o/$MODE/cosmopolitan.a"
CPPFLAGS="$CPPFLAGS -isystem $COSMOS/include -isystem $COSMO/libc/isystem"
LDFLAGS="$LDFLAGS -L$COSMOS/lib"
if [ x"$PROG" != x"${PROG%++}" ]; then
CC="$COSMO/o/third_party/gcc/bin/$ARCH-linux-musl-g++"
CFLAGS="$CFLAGS -fno-rtti -fno-exceptions -fuse-cxa-atexit"
LDLIBS="$COSMO/o/$MODE/third_party/libcxx/libcxx.a $LDLIBS"
fi
PAGESZ=4096
if [ x"$ARCH" = x"x86_64" ]; then
OBJCOPYFLAGS="-S -O binary"
CRT="$COSMO/o/$MODE/ape/ape-no-modify-self.o $CRT"
CPPFLAGS="$CPPFLAGS -mno-red-zone"
CFLAGS="$CFLAGS -mno-tls-direct-seg-refs"
LDFLAGS="$LDFLAGS -Wl,-T,$COSMO/o/$MODE/ape/ape.lds"
if [ x"$MODE" = x"aarch64" ]; then
fatal_error '$MODE must not be aarch64 when using x86_64 cross compiler'
elif [ x"$MODE" != x"${MODE#* }" ]; then
fatal_error '$MODE must not contain hyphens when targeting x86_64'
fi
elif [ x"$ARCH" = x"aarch64" ]; then
OBJCOPYFLAGS="-S"
PAGESZ=16384
CFLAGS="$CFLAGS -ffixed-x18 -ffixed-x28 -mno-outline-atomics"
LDFLAGS="$LDFLAGS -Wl,-T,$COSMO/o/$MODE/ape/aarch64.lds"
if [ x"$MODE" != x"aarch64" ] && [ x"$MODE" = x"${MODE#aarch64-*}" ]; then
fatal_error '$MODE must be either "aarch64" or "aarch64-FOO" when using aarch64 cross compiler'
fi
else
fatal_error "$ARCH: unsupported architecture"
fi
LDFLAGS="$LDFLAGS -Wl,-z,common-page-size=$PAGESZ -Wl,-z,max-page-size=16384"
if [ ! -d "$COSMO" ]; then
fatal_error "you need to clone cosmopolitan to your $COSMO directory"
fi
if [ "$1" = "--update" ]; then
cd $COSMO || exit
echo "building cosmo host toolchain..." >&2
make --silent -j toolchain MODE= || exit
echo "building cosmo target (MODE=$MODE) toolchain..." >&2
make --silent -j toolchain MODE="$MODE" || exit
echo "setting up your cosmos..." >&2
mkdir -p "$COSMOS/lib" || exit
for lib in c dl gcc_s m pthread resolv rt dl z stdc++; do
if [ ! -f "$COSMOS/lib/lib${lib}.a" ]; then
printf '\041\074\141\162\143\150\076\012' >"$COSMOS/lib/lib${lib}.a" || exit
fi
done
echo "successfully updated your cosmo toolchain" >&2
exit
fi
if [ ! -f "$COSMOS/lib/libc.a" ] ||
[ ! -f "$COSMO/o/$MODE/cosmopolitan.a" ]; then
fatal_error "you need to run: $PROG --update"
fi
OPT=
FIRST=1
OUTPUT=
SFLAG=0
INTENT=ld
GOT_SOME=0
NEED_OUTPUT=
RELOCATABLE=0
for x; do
if [ $FIRST -eq 1 ]; then
set --
FIRST=0
fi
if [ -n "$NEED_OUTPUT" ]; then
NEED_OUTPUT=
OUTPUT=$x
set -- "$@" "$x"
continue
fi
if [ x"$x" = x"-" ] || # is an argument
[ x"$x" = x"${x#-*}" ]; then # !startswith(x, "-")
GOT_SOME=1
elif [ x"$x" = x"-static-libstdc++" ]; then
continue
elif [ x"$x" = x"-static-libgcc" ]; then
continue
elif [ x"$x" != x"${x#-O}" ]; then
OPT=$x
elif [ x"$x" = x"-c" ]; then
INTENT=cc
elif [ x"$x" = x"-s" ]; then
SFLAG=1
continue
elif [ x"$x" = x"-r" ]; then
RELOCATABLE=1
elif [ x"$x" = x"-E" ]; then
INTENT=cpp
elif [ x"$x" = x"-o" ]; then
NEED_OUTPUT=1
elif [ x"$x" = x"-mcosmo" ]; then
CPPFLAGS="$CPPFLAGS -D_COSMO_SOURCE"
continue
elif [ x"$x" != x"${x#-o}" ]; then
OUTPUT=${x#-o}
elif [ x"$x" = x"-fpic" ]; then
continue
elif [ x"$x" = x"-fPIC" ]; then
continue
elif [ x"$x" = x"-r" ] ||
[ x"$x" = x"-pie" ] ||
[ x"$x" = x"-frtti" ] ||
[ x"$x" = x"-shared" ] ||
[ x"$x" = x"-nostdlib" ] ||
[ x"$x" = x"-mred-zone" ] ||
[ x"$x" = x"-fexceptions" ] ||
[ x"$x" = x"-fsanitize=thread" ]; then
echo "$PROG: $x not supported" >&2
exit 1
elif [ x"$x" = x"-fsanitize=all" ] ||
[ x"$x" = x"-fsanitize=address" ] ||
[ x"$x" = x"-fsanitize=undefined" ]; then
echo "$PROG: use cosmo MODE=dbg or MODE=asan rather than passing $x" >&2
exit 1
elif [ x"$x" = x"-fomit-frame-pointer" ]; then
# Quoth Apple "The frame pointer register must always address a
# valid frame record. Some functions — such as leaf functions or
# tail calls — may opt not to create an entry in this list. As a
# result, stack traces are always meaningful, even without debug
# information."
set -- "$@" -momit-leaf-frame-pointer -foptimize-sibling-calls
continue
elif [ x"$x" != x"${x#-Werror}" ] || \
[ x"$x" = x"-pedantic-errors" ]; then
# this toolchain is intended for building other people's code
# elevating warnings into errors, should only be done by devs
continue
elif [ x"$x" = x"-march=native" ]; then
if [ $CROSS -eq 0 ]; then
set -- "$@" $("$COSMO/o//tool/build/march-native.com")
else
fatal_error "-march=native can't be used when cross compiling"
fi
continue
elif [ x"$x" = x"-dumpversion" ]; then
echo $GCC_VERSION
exit 0
fi
set -- "$@" "$x"
done
if [ "$GOT_SOME" -eq 0 ]; then
fatal_error "no input files"
fi
if [ $RELOCATABLE -eq 1 ]; then
LDFLAGS="$LDFLAGS -r"
fi
if [ x"$MODE" = x"nox87" ]; then
CPPFLAGS="$CPPFLAGS -mlong-double-64"
fi
# support --ftrace unless optimizing for size
if [ x"$OPT" != x"-Os" ] && # $OPT != -Os
[ x"${MODE%tiny}" = x"${MODE}" ]; then # $MODE not in (tiny, aarch64-tiny)
if [ x"$ARCH" = x"x86_64" ]; then
CFLAGS="$CFLAGS -fpatchable-function-entry=18,16"
elif [ x"$ARCH" = x"aarch64" ]; then
CFLAGS="$CFLAGS -fpatchable-function-entry=7,6"
fi
fi
# ask compiler to generate sanitization code in debug mode
if [ x"$MODE" != x"${MODE%*dbg}" ]; then # endswith($MODE, "dbg")
if [ x"$ARCH" = x"x86_64" ]; then
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
else
CPPFLAGS="$CPPFLAGS -fsanitize=undefined"
fi
fi
# ask compiler to generate memory safety code in asan mode
if [ x"$MODE" != x"${MODE%*asan}" ]; then # endswith($MODE, "asan")
if [ x"$ARCH" = x"x86_64" ]; then
CPPFLAGS="$CPPFLAGS -fsanitize=address"
else
fatal_error "address sanitizer not supported on non-x86 yet"
fi
fi
# maximize frame pointers unless optimizing for size
if [ x"$OPT" != x"-Os" ] && # $OPT != "-Os"
[ x"$MODE" != x"${MODE%tiny}" ]; then # endswith($MODE, "tiny")
CFLAGS="$CFLAGS -fno-optimize-sibling-calls -mno-omit-leaf-frame-pointer"
fi
if [ $INTENT = cpp ]; then
set -- "$CC" $PLATFORM $CPPFLAGS "$@"
elif [ $INTENT = cc ]; then
set -- "$CC" $PLATFORM $PREDEF $CFLAGS $CPPFLAGS "$@" $PRECIOUS
else
set -- "$CC" $PLATFORM $PREDEF $CFLAGS $CPPFLAGS $CRT "$@" $LDFLAGS $LDLIBS $PRECIOUS
fi
log_command "$@"
"$@" || exit
if [ -n "$OUTPUT" ] && [ -f "$OUTPUT" ]; then
if [ $INTENT = cc ] || [ $INTENT = ld ]; then
"$COSMO/o//tool/build/fixupobj.com" \
"$OUTPUT" || exit
fi
if [ $INTENT = ld ]; then
if [ x"$OUTPUT" != x"${OUTPUT%.com}" ] ||
[ x"$OUTPUT" != x"${OUTPUT%.exe}" ]; then
# cosmocc -o foo.com ...
# -> foo.com (ape)
# -> foo.com.dbg (elf)
mv -f "$OUTPUT" "$OUTPUT.dbg" || exit
"$COSMO/o/third_party/gcc/bin/$ARCH-linux-musl-objcopy" \
$OBJCOPYFLAGS \
"$OUTPUT.dbg" \
"$OUTPUT" || exit
"$COSMO/o//tool/build/zipcopy.com" \
"$OUTPUT.dbg" \
"$OUTPUT" || exit
elif [ $SFLAG -eq 1 ]; then
"$COSMO/o/third_party/gcc/bin/$ARCH-linux-musl-strip" \
"$OUTPUT" || exit
fi
fi
fi