Skip to content

Instantly share code, notes, and snippets.

@avih
Last active May 2, 2023 12:20
Show Gist options
  • Save avih/467b307051bf01bc9c8475221a59513d to your computer and use it in GitHub Desktop.
Save avih/467b307051bf01bc9c8475221a59513d to your computer and use it in GitHub Desktop.
cosmocc script to simplify usage of the cosmopolitan.zip bundle
#!/bin/sh
[ "${COSMOXX-}" ] && set -x || :
# name: cosmocc
# self-contained replacement for the cc command which uses cosmopolitan libc
# see https://justine.lol/cosmopolitan/index.html
# home: https://gist.github.com/avih/467b307051bf01bc9c8475221a59513d
# based on: https://github.com/jart/cosmopolitan/blob/master/tool/scripts/cosmocc
# requires: gcc or clang installed
#
# how to use:
# download a cosmopolitan[-variant].zip bundle of your choice from here
# https://justine.lol/cosmopolitan/download.html then extract it to some
# dir X, place this file in X, ensure it's executable, link to it from PATH
#
# or, create a dir X where the cosmopolitan files will be stored, then
# copy this script into X, and link to it from someplace at PATH.
# this will download the main cosmopolitan.zip bundle on first run.
#
# or, set COSMO below to an absolute dir path which will hold the files,
# and place or link to this file someplace at PATH. the dir will be created
# automatically on first run, before downloading the zip.
#
# E.g., assuming ~/.local/bin is in PATH, and that this file
# is ~/cosmocc and executable:
# mkdir ~/cosmo && cp ~/cosmocc ~/cosmo/ && ln -s ~/cosmo/cosmocc ~/.local/bin/
#
# then use "cosmocc" instead of "cc", e.g:
# cosmocc myapp.c -o myapp.com.dbg
# or:
# cosmocc -c file1.c file2.c && cosmocc file1.o file2.o -o myapp.com.dbg
# or:
# make CC=cosmocc ... && cosmocc --cosmo2ape path/to/myapp
# etc.
#
#
# first run does initial setup - create the dir if needed,
# download and extract the cosmopolitan zip if needed, setup some
# default include wrappers and shell lib files in that dir.
#
# when creating binaries, e.g. cosmocc file.c -o myapp
# then the resulting file runs on linux but is not actually APE.
# it needs an additional step: objcopy -S -O binary myapp myapp.com
# if the binary has a .com.dbg extension, then two things will happen:
# - the objcopy command is invoked automatically to create a .com APE file
# - the .com APE file will use the symbols at the .com.dbg file
# when tracing functions, e.g. myapp.com --ftrace ...
# (with default or "dbg" cosmopolitan.zip variants, but not "tiny")
# for instance: cosmocc file.c -o file.com.dbg
# creates file.com too, which can use the debug symbols at file.com.dbg
# NOTE: don't use strip on the resulting binaries.
#
# If the binary was created using a Makefile or some other build system
# and the application ends up without .com.dbg suffix, then this command will
# rename it and also create the APE file: cosmocc --cosmo2ape path/to/myapp
#
# ENV vars:
# COSMOCC=... set a specific compiler to use. default is gcc or clang or cc
# COSMOX=1 trace the compiler commands
# COSMOXX=1 trace the whole script
# if readlink -f is unavailable, or if this file is outside the cosmo dir
# then set COSMO manually to the full path to the dir (without trail /)
COSMO=$(dirname -- "$(readlink -f -- "$0")")
one_val() { [ "$#" = 1 ] && [ "$1" ]; }
one_val $COSMO ||
{ >&2 echo "$0: \$COSMO must be non-nul, no spaces -- '$COSMO'"; exit 1; }
if [ "${1-}" = --cosmo2ape ]; then
shift
[ "${1-}" != -- ] || shift
[ -x "${1-}" ] ||
{ echo "Usage: $0 --cosmo2ape APP... cosmo binary -> APE"; exit 1; }
( set -x
for f; do
mv -- "$f" "$f.com.dbg"
objcopy -S -O binary -- "$f.com.dbg" "$f.com"
done
); exit
fi
# download the archive if missing
[ -e "$COSMO"/cosmopolitan.a ] || (
# for variants, see: https://justine.lol/cosmopolitan/download.html
variant= # <empty> | tiny | tinylinux | rel | asan | dbg
f=cosmopolitan${variant:+-$variant}.zip
mkdir -p -- "$COSMO" &&
cd "$COSMO" &&
{ ! [ -e "$f" ] || mv -- "$f" "prev.$f"; } &&
wget -- "https://justine.lol/cosmopolitan/$f" &&
unzip -- "$f" &&
rm -rf lib include
) || exit
# ensure shell includes
posix_includes="
aio.h arpa/inet.h assert.h complex.h cpio.h ctype.h dirent.h dlfcn.h
errno.h fcntl.h fenv.h float.h fmtmsg.h fnmatch.h ftw.h glob.h grp.h
iconv.h inttypes.h iso646.h langinfo.h libgen.h limits.h locale.h
math.h monetary.h mqueue.h ndbm.h net/if.h netdb.h netinet/in.h
netinet/tcp.h nl_types.h poll.h pthread.h pwd.h regex.h sched.h
search.h semaphore.h setjmp.h signal.h spawn.h stdarg.h stdbool.h
stddef.h stdint.h stdio.h stdlib.h string.h strings.h stropts.h
sys/ipc.h sys/mman.h sys/msg.h sys/resource.h sys/select.h sys/sem.h
sys/shm.h sys/socket.h sys/stat.h sys/statvfs.h sys/time.h
sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h
syslog.h tar.h termios.h tgmath.h time.h trace.h ulimit.h unistd.h
utime.h utmpx.h wchar.h wctype.h wordexp.h
"
if ! [ -d "$COSMO/include" ]; then
mkdir -p "$COSMO/include"
for inc in $posix_includes; do
case $inc in */*) mkdir -p $COSMO/include/${inc%/*}; esac
ln -s -- "$COSMO/cosmopolitan.h" "$COSMO/include/$inc" 2>/dev/null ||
printf %s\\n "#include \"$COSMO/cosmopolitan.h\"" >"$COSMO/include/$inc"
done
fi
# the following is adapted from:
# https://github.com/jart/cosmopolitan/blob/master/tool/scripts/cosmocc
# auto-install some shell libraries
if [ ! -d $COSMO/lib ]; then
mkdir $COSMO/lib
# FIXME: other than libc, which of those actually exist at cosmopolitan.a?
for lib in c dl gcc_s m pthread resolv rt z stdc++; do
printf '\041\074\141\162\143\150\076\012' >$COSMO/lib/lib$lib.a
done
fi
if [ "$1" = "--version" ]; then
cat <<'EOF'
x86_64-unknown-cosmo-gcc (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF
exit 0
fi
has_nop_mcount() {
echo "int main() {return 0;}" |
"$1" -c -xc - -o /dev/null -mnop-mcount 2>/dev/null
}
: ${COSMOCC:=$(which gcc || which clang || which cc)}
has_nop_mcount "$COSMOCC" && NOPMCOUNT=-mnop-mcount || NOPMCOUNT=
CFLAGS="-g -O2 -fdata-sections -ffunction-sections -fno-pie -pg $NOPMCOUNT \
-mno-tls-direct-seg-refs"
CPPFLAGS="-DNDEBUG -nostdinc -isystem $COSMO/include"
LDFLAGS="-static -no-pie -nostdlib -fuse-ld=bfd -Wl,-melf_x86_64 \
-Wl,--gc-sections -Wl,-z,max-page-size=0x1000 \
-L$COSMO/lib -Wl,-T,$COSMO/ape.lds \
$COSMO/ape-no-modify-self.o $COSMO/crt.o"
LDLIBS=$COSMO/cosmopolitan.a
HAS_C= HAS_E=
O= NEXT_IS_O=
for x; do shift
if [ "$NEXT_IS_O" ]; then
O=$x NEXT_IS_O=
set -- "$@" "$x"
continue
fi
case $x in
-Werror|-pedantic)
# this toolchain is intended for building other people's code
# elevating warnings into errors, should only be done by devs
# we don't need the compiler's assistance to be more portable
>&2 printf %s\\n "$0: ignoring $x"
continue
;;
-c) HAS_C=x ;;
-E) HAS_E=x ;;
-o) NEXT_IS_O=x ;;
-o*) O=${x#-o} ;;
esac
set -- "$@" "$x"
done
if [ "$HAS_E" ]; then
set -- $CPPFLAGS "$@"
elif [ "$HAS_C" ]; then
set -- $CFLAGS $CPPFLAGS "$@" -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer
else # [compile and] link
set -- $LDFLAGS $CFLAGS $CPPFLAGS "$@" $LDLIBS -Wl,-z,common-page-size=4096 -Wl,-z,max-page-size=4096
fi
set -- "$COSMOCC" "$@"
printf '(cd %s; %s)\n' "$PWD" "$*" >>/tmp/$(basename -- "$0").log
[ "${COSMOX-}" ] && set -x || :
"$@" && {
if [ -x "$O" ]; then
case $O in *.com.dbg)
out=${O%.dbg}
>&2 printf %s\\n "[auto: $O --> $out]"
objcopy -S -O binary "$O" "$out"
esac
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment