Helper script
Because the command line required to compile exercises is quite unwieldy, we've created a wrapper script to help out, shown below. If you've checked out this repository it's present in tools/ccc
. The usage is:
ccc <arch> [...]
Supported architectures:
aarch64 - conventional AArch64
morello-hybrid - AArch64 Morello supporting CHERI
morello-purecap - AArch64 Morello pure-capability
riscv64 - conventional RISC-V 64-bit
riscv64-hybrid - RISC-V 64-bit supporting CHERI
riscv64-purecap - RISC-V 64-bit pure-capability
and it can be used in place of your compiler.
For the exercises in this book you will use the riscv64
and riscv64-purecap
architectures. The riscv64-hybrid
architecture instantiates appropriately annotated pointers as capabilities leaving the rest as conventional integer addresses, but is not used here.
If you have built a compiler and sysroot using cheribuild
in the default location (~/cheri
) then it should work out of the box. If you've configured a different location you can set the CHERIBUILD_SDK
environment variable to point to to the location of your SDK. Alternatively, you can set the CLANG
and SYSROOT
variables to point to the respective locations.
#!/bin/sh
#
# ccc - Cross compilation script
set -e
set -u
name=$(basename "$0")
VERBOSE=${VERBOSE:-0}
QUIET=${QUIET:-0}
usage()
{
cat <<EOF
$name <arch> [...]
Supported architectures:
aarch64 - conventional AArch64
morello-hybrid - AArch64 Morello supporting CHERI
morello-purecap - AArch64 Morello pure-capability
riscv64 - conventional RISC-V 64-bit
riscv64-hybrid - RISC-V 64-bit supporting CHERI
riscv64-purecap - RISC-V 64-bit pure-capability
EOF
exit 1
}
err()
{
ret=$1
shift
echo >&2 "$@"
exit "$ret"
}
warn()
{
echo >&2 "$@"
}
debug()
{
if [ "$VERBOSE" -ne 0 ]; then
echo >&2 "$@"
fi
}
info()
{
if [ "$QUIET" -eq 0 ]; then
echo >&2 "$@"
fi
}
run()
{
debug # add space before normal multiline output
info "Running:" "$@"
"$@"
}
if [ $# -eq 0 ]; then
usage
fi
arch=$1
shift
cheri_arch_basename=${arch%%-*}
cheri_sdk_name=sdk
case $arch in
aarch64)
cheri_arch_basename=morello
cheri_sdk_name=morello-sdk
arch_flags="-target aarch64-unknown-freebsd -march=morello+noa64c"
;;
morello-hybrid)
cheri_sdk_name=morello-sdk
arch_flags="-target aarch64-unknown-freebsd -march=morello -Xclang -morello-vararg=new"
;;
morello-purecap)
cheri_sdk_name=morello-sdk
arch_flags="-target aarch64-unknown-freebsd -march=morello -mabi=purecap -Xclang -morello-vararg=new"
;;
riscv64)
arch_flags="-target riscv64-unknown-freebsd -march=rv64gc -mabi=lp64d -mno-relax"
;;
riscv64-hybrid)
arch_flags="-target riscv64-unknown-freebsd -march=rv64gcxcheri -mabi=lp64d -mno-relax"
;;
riscv64-purecap)
arch_flags="-target riscv64-unknown-freebsd -march=rv64gcxcheri -mabi=l64pc128d -mno-relax"
;;
*)
err 1 "Unsupported architecture '$arch'"
;;
esac
# Find our SDK, using the first of these that expands only defined variables:
# ${CHERIBUILD_SDK_${cheri_sdk_name}} (if that syntax worked)
# ${CHERIBUILD_SDK}
# ${CHERIBUILD_OUTPUT}/${cheri_sdk_name}
# ${CHERIBUILD_SOURCE}/output/${cheri_sdk_name}
# ~/cheri/output/${cheri_sdk_name}
SDKDIR_SOURCE=${CHERIBUILD_SOURCE:-${HOME}/cheri}
SDKDIR_OUTPUT=${CHERIBUILD_OUTPUT:-${SDKDIR_SOURCE}/output}
SDKDIR_SDK=${CHERIBUILD_SDK:-${SDKDIR_OUTPUT}/${cheri_sdk_name}}
SDKDIR=$(eval echo \${CHERIBUILD_SDK_"${cheri_arch_basename}":-})
SDKDIR=${SDKDIR:-${SDKDIR_SDK}}
enverr()
{
echo >&2 $1
echo "Perhaps set or adjust one of the following environment variables:"
for v in SOURCE OUTPUT SDK; do
echo " " CHERIBUILD_$v \(currently: \
$(eval echo \${CHERIBUILD_$v:-unset, tried \$SDKDIR_$v})\)
done
A="CHERIBUILD_SDK_${cheri_arch_basename}"
echo " " "$A" \(currently: $(eval echo \${$A:-unset, tried \$SDKDIR})\)
echo " " "$2" \(currently: $(eval echo \${$2:-unset, tried \$SDK_$2})\)
err 1 "Please check your build environment"
}
SDK_CLANG=${CLANG:-${SDKDIR}/bin/clang}
case $name in
*clang|*cc) prog="${SDK_CLANG}" ;;
*clang++|*c++) prog="${SDK_CLANG}++" ;;
*) err 1 "Unsupported program name '$name'" ;;
esac
if [ ! -x "$prog" ]; then
enverr "Target compiler '$prog' not found." "CLANG"
fi
debug "prog: $prog"
SDK_SYSROOT=${SYSROOT:-${SDKDIR}/sysroot-${cheri_arch_basename}-purecap}
if [ ! -d "$SDK_SYSROOT" ]; then
enverr "Sysroot '$SDK_SYSROOT' does not exist." "SYSROOT"
fi
debug "sysroot: $SDK_SYSROOT"
debug "arch_flags: $arch_flags"
debug_flags="-g"
debug "debug_flags: $debug_flags"
opt_flags="-O2"
debug "opt_flags: $opt_flags"
sysroot_flags="--sysroot='$SDK_SYSROOT'"
debug "sysroot_flags: $sysroot_flags"
linker_flags="-fuse-ld=lld"
debug "linker_flags: $linker_flags"
diag_flags="-Wall -Wcheri"
debug "diag_flags: $diag_flags"
all_flags="$arch_flags $sysroot_flags $debug_flags $opt_flags $linker_flags $diag_flags"
all_flags_rev=
# shellcheck disable=SC2086 # intentional
eval 'for flag in '$all_flags'; do
all_flags_rev="'"'"'$flag'"'"'${all_flags_rev:+ $all_flags_rev}"
done'
# shellcheck disable=SC2086 # intentional
eval 'for flag in '$all_flags_rev'; do
set -- "$flag" "$@"
done'
run "$prog" "$@"
If you were provided a docker image along with these instructions (e.g. as part of a training exercise or bug-bounty), it should be configured such that ccc
works without setting environment variables.
Although not used by these exercises, the tool will instead function as a C++ compiler if invoked via the name cc++
, and a tools/cc++
symlink exists to facilitate this.