blob: 45019cc839ef98be01572db537d92bd7b039c384 [file] [log] [blame]
#!/bin/sh
#
# american fuzzy lop++ - QEMU build script
# --------------------------------------
#
# Originally written by Andrew Griffiths <agriffiths@google.com> and
# Michal Zalewski
#
# TCG instrumentation and block chaining support by Andrea Biondo
# <andrea.biondo965@gmail.com>
#
# QEMU 5+ port, TCG thread-safety, CompareCoverage and NeverZero
# counters by Andrea Fioraldi <andreafioraldi@gmail.com>
#
# Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
# Copyright 2019-2024 AFLplusplus Project. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# This script downloads, patches, and builds a version of QEMU with
# minor tweaks to allow non-instrumented binaries to be run under
# afl-fuzz.
#
# The modifications reside in patches/*. The standalone QEMU binary
# will be written to ../afl-qemu-trace.
#
QEMUAFL_VERSION="$(cat ./QEMUAFL_VERSION)"
echo "================================================="
echo " QemuAFL build script"
echo "================================================="
echo
echo "[*] Performing basic sanity checks..."
if [ ! "`uname -s`" = "Linux" ]; then
echo "[-] Error: QEMU instrumentation is supported only on Linux."
exit 0
fi
if [ ! -f "../config.h" ]; then
echo "[-] Error: key files not found - wrong working directory?"
exit 1
fi
if [ ! -f "../afl-showmap" ]; then
echo "[-] Error: ../afl-showmap not found - compile AFL first!"
exit 1
fi
if echo "$CC" | grep -qF /afl-; then
echo "[-] Error: do not use afl-gcc or afl-clang to compile this tool."
exit 1
fi
echo "[+] All checks passed!"
echo "[*] Making sure qemuafl is checked out"
git status 1>/dev/null 2>/dev/null
if [ $? -eq 0 ]; then
echo "[*] initializing qemuafl submodule"
git submodule init || exit 1
git submodule update ./qemuafl 2>/dev/null # ignore errors
else
echo "[*] cloning qemuafl"
test -d qemuafl/.git || {
CNT=1
while [ '!' -d qemuafl/.git -a "$CNT" -lt 4 ]; do
echo "Trying to clone qemuafl (attempt $CNT/3)"
git clone --depth 1 https://github.com/AFLplusplus/qemuafl
CNT=`expr "$CNT" + 1`
done
}
fi
test -e qemuafl/.git || { echo "[-] Not checked out, please install git or check your internet connection." ; exit 1 ; }
echo "[+] Got qemuafl."
cd "qemuafl" || exit 1
if [ -n "$NO_CHECKOUT" ]; then
echo "[*] Skipping checkout to $QEMUAFL_VERSION"
else
echo "[*] Checking out $QEMUAFL_VERSION"
sh -c 'git stash' 1>/dev/null 2>/dev/null
git pull
git checkout "$QEMUAFL_VERSION" || echo Warning: could not check out to commit $QEMUAFL_VERSION
fi
echo "[*] Making sure imported headers matches"
cp "../../include/config.h" "./qemuafl/imported/" || exit 1
cp "../../include/cmplog.h" "./qemuafl/imported/" || exit 1
cp "../../include/snapshot-inl.h" "./qemuafl/imported/" || exit 1
cp "../../include/types.h" "./qemuafl/imported/" || exit 1
if [ -n "$HOST" ]; then
echo "[+] Configuring host architecture to $HOST..."
CROSS_PREFIX=$HOST-
else
CROSS_PREFIX=
fi
echo "[*] Configuring QEMU for $CPU_TARGET..."
ORIG_CPU_TARGET="$CPU_TARGET"
if [ "$ORIG_CPU_TARGET" = "" ]; then
CPU_TARGET="`uname -m`"
test "$CPU_TARGET" = "i686" && CPU_TARGET="i386"
test "$CPU_TARGET" = "arm64v8" && CPU_TARGET="aarch64"
case "$CPU_TARGET" in
*arm*)
CPU_TARGET="arm"
;;
esac
fi
echo "Building for CPU target $CPU_TARGET"
# --enable-pie seems to give a couple of exec's a second performance
# improvement, much to my surprise. Not sure how universal this is..
# --enable-plugins allows loading TCG plugins at runtime, for example to obtain
# coverage information, and does not seem to negatively impact performance
QEMU_CONF_FLAGS=" \
--enable-plugins \
--audio-drv-list= \
--disable-blobs \
--disable-bochs \
--disable-brlapi \
--disable-bsd-user \
--disable-bzip2 \
--disable-cap-ng \
--disable-cloop \
--disable-curl \
--disable-curses \
--disable-dmg \
--disable-fdt \
--disable-gcrypt \
--disable-glusterfs \
--disable-gnutls \
--disable-gtk \
--disable-guest-agent \
--disable-iconv \
--disable-libiscsi \
--disable-libnfs \
--disable-libssh \
--disable-libusb \
--disable-linux-aio \
--disable-live-block-migration \
--disable-lzo \
--disable-nettle \
--disable-numa \
--disable-opengl \
--disable-parallels \
--disable-qcow1 \
--disable-qed \
--disable-rbd \
--disable-rdma \
--disable-replication \
--disable-sdl \
--disable-seccomp \
--disable-sheepdog \
--disable-smartcard \
--disable-snappy \
--disable-spice \
--disable-system \
--disable-tools \
--disable-tpm \
--disable-usb-redir \
--disable-vde \
--disable-vdi \
--disable-vhost-crypto \
--disable-vhost-kernel \
--disable-vhost-net \
--disable-vhost-scsi \
--disable-vhost-user \
--disable-vhost-vdpa \
--disable-vhost-vsock \
--disable-virglrenderer \
--disable-virtfs \
--disable-vnc \
--disable-vnc-jpeg \
--disable-vnc-png \
--disable-vnc-sasl \
--disable-vte \
--disable-vvfat \
--disable-xen \
--disable-xen-pci-passthrough \
--disable-xfsctl \
--target-list="${CPU_TARGET}-linux-user" \
--without-default-devices \
"
if [ -n "${CROSS_PREFIX}" ]; then
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS --cross-prefix=$CROSS_PREFIX"
fi
if [ "$STATIC" = "1" ]; then
echo Building STATIC binary
# static PIE causes https://github.com/AFLplusplus/AFLplusplus/issues/892
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
--static --disable-pie \
--extra-cflags=-DAFL_QEMU_STATIC_BUILD=1 \
"
else
QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} --enable-pie "
fi
if [ "$DEBUG" = "1" ]; then
echo Building DEBUG binary
# --enable-gcov might go here but incurs a mesonbuild error on meson
# versions prior to 0.56:
# https://github.com/qemu/meson/commit/903d5dd8a7dc1d6f8bef79e66d6ebc07c
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
--disable-strip \
--enable-debug \
--enable-debug-info \
--enable-debug-mutex \
--enable-debug-stack-usage \
--enable-debug-tcg \
--enable-qom-cast-debug \
--enable-werror \
"
else
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
--disable-debug-info \
--disable-debug-mutex \
--disable-debug-tcg \
--disable-qom-cast-debug \
--disable-stack-protector \
--disable-werror \
--disable-docs \
"
fi
if [ "$PROFILING" = "1" ]; then
echo Building PROFILED binary
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
--enable-gprof \
--enable-profiler \
"
fi
# shellcheck disable=SC2086
./configure $QEMU_CONF_FLAGS || exit 1
echo "[+] Configuration complete."
echo "[*] Attempting to build QEMU (fingers crossed!)..."
make -j$(nproc) || exit 1
echo "[+] Build process successful!"
echo "[*] Copying binary..."
cp -f "build/${CPU_TARGET}-linux-user/qemu-${CPU_TARGET}" "../../afl-qemu-trace" || exit 1
cd ..
ls -l ../afl-qemu-trace || exit 1
echo "[+] Successfully created '../afl-qemu-trace'."
if [ "$ORIG_CPU_TARGET" = "" ]; then
echo "[*] Testing the build..."
cd ..
make >/dev/null || exit 1
cc test-instr.c -o test-instr || exit 1
unset AFL_INST_RATIO
export ASAN_OPTIONS=detect_leaks=0
echo "[*] Comparing two afl-showmap -Q outputs..."
echo 0 | ./afl-showmap -m none -Q -q -o .test-instr0 ./test-instr || exit 1
echo 1 | ./afl-showmap -m none -Q -q -o .test-instr1 ./test-instr || exit 1
rm -f test-instr
cmp -s .test-instr0 .test-instr1
DR="$?"
rm -f .test-instr0 .test-instr1
if [ "$DR" = "0" ]; then
echo "[-] Error: afl-qemu-trace instrumentation doesn't seem to work!"
exit 1
fi
echo "[+] Instrumentation tests passed. "
echo "[+] All set, you can now use the -Q mode in afl-fuzz!"
cd qemu_mode || exit 1
else
echo "[!] Note: can't test instrumentation when CPU_TARGET set."
echo "[+] All set, you can now (hopefully) use the -Q mode in afl-fuzz!"
fi
ORIG_CROSS="$CROSS"
if [ "$ORIG_CROSS" = "" ]; then
CROSS=$CPU_TARGET-linux-gnu-gcc
if ! command -v "$CROSS" > /dev/null
then # works on Arch Linux
CROSS=$CPU_TARGET-pc-linux-gnu-gcc
fi
if ! command -v "$CROSS" > /dev/null && [ "$CPU_TARGET" = "i386" ]
then
CROSS=i686-linux-gnu-gcc
if ! command -v "$CROSS" > /dev/null
then # works on Arch Linux
CROSS=i686-pc-linux-gnu-gcc
fi
if ! command -v "$CROSS" > /dev/null && [ "`uname -m`" = "x86_64" ]
then # set -m32
test "$CC" = "" && CC="gcc"
CROSS="$CC"
CROSS_FLAGS=-m32
fi
fi
fi
if ! command -v "$CROSS" > /dev/null ; then
if [ "$CPU_TARGET" = "$(uname -m)" ] ; then
echo "[+] Building AFL++ qemu support libraries with CC=$CC"
echo "[+] Building libcompcov ..."
make -C libcompcov && echo "[+] libcompcov ready"
echo "[+] Building unsigaction ..."
make -C unsigaction && echo "[+] unsigaction ready"
echo "[+] Building fastexit ..."
make -C fastexit && echo "[+] fastexit ready"
echo "[+] Building libqasan ..."
make -C libqasan && echo "[+] libqasan ready"
echo "[+] Building qemu libfuzzer helpers ..."
make -C ../utils/aflpp_driver
else
echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction"
fi
else
echo "[+] Building AFL++ qemu support libraries with CC=\"$CROSS $CROSS_FLAGS\""
echo "[+] Building libcompcov ..."
make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready"
echo "[+] Building unsigaction ..."
make -C unsigaction CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready"
echo "[+] Building fastexit ..."
make -C fastexit CC="$CROSS $CROSS_FLAGS" && echo "[+] fastexit ready"
echo "[+] Building libqasan ..."
make -C libqasan CC="$CROSS $CROSS_FLAGS" && echo "[+] libqasan ready"
fi
echo "[+] All done for qemu_mode, enjoy!"
exit 0