about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2024-01-18 16:17:48 +0100
committerGitHub <noreply@github.com>2024-01-18 15:17:48 +0000
commit0c054f520eda67b7bb15f95ca58c028e9b68131f (patch)
tree456d0f665db85412a35f1c66c2991250098c4970
parent358cd1b062e58ce1d5c8efeef4789a5aca7ac5a9 (diff)
downloadafl++-0c054f520eda67b7bb15f95ca58c028e9b68131f.tar.gz
push to stable (#1960)
* Output afl-clang-fast stuffs only if necessary (#1912)

* afl-cc header

* afl-cc common declarations

 - Add afl-cc-state.c
 - Strip includes, find_object, debug/be_quiet/have_*/callname setting from afl-cc.c
 - Use debugf_args in main
 - Modify execvp stuffs to fit new aflcc struct

* afl-cc show usage

* afl-cc mode selecting

1. compiler_mode by callname in argv[0]
2. compiler_mode by env "AFL_CC_COMPILER"
3. compiler_mode/instrument_mode by command line options "--afl-..."
4. instrument_mode/compiler_mode by various env vars including "AFL_LLVM_INSTRUMENT"
5. final checking steps
6. print "... - mode: %s-%s\n"
7. determine real argv[0] according to compiler_mode

* afl-cc macro defs

* afl-cc linking behaviors

* afl-cc fsanitize behaviors

* afl-cc misc

* afl-cc body update

* afl-cc all-in-one

formated with custom-format.py

* nits

---------

Co-authored-by: vanhauser-thc <vh@thc.org>

* changelog

* update grammar mutator

* lto llvm 12+

* docs(custom_mutators): fix missing ':' (#1953)

* Fix broken LTO mode and response file support (#1948)

* Strip `-Wl,-no-undefined` during compilation (#1952)

Make the compiler wrapper stripping `-Wl,-no-undefined` in addition to `-Wl,--no-undefined`.
Both versions of the flag are accepted by clang and, therefore, used by building systems in the wild (e.g., samba will not build without this fix).

* Remove dead code in write_to_testcase (#1955)

The custom_mutators_count check in if case is duplicate with if condition.
The else case is custom_mutators_count == 0, neither custom_mutator_list iteration nor sent check needed.

Signed-off-by: Xeonacid <h.dwwwwww@gmail.com>

* update qemuafl

* WIP: Add ability to generate drcov trace using QEMU backend (#1956)

* Document new drcov QEMU plugin

* Add link to lightkeeper for QEMU drcov file loading

---------

Co-authored-by: Jean-Romain Garnier <jean-romain.garnier@airbus.com>

* code format

* changelog

* sleep on uid != 0 afl-system-config

* fix segv about skip_next, warn on unsupported cases of linking options (#1958)

* todos

* ensure afl-cc only allows available compiler modes

* update grammar mutator

* disable aslr on apple

* fix for arm64

---------

Signed-off-by: Xeonacid <h.dwwwwww@gmail.com>
Co-authored-by: Sonic <50692172+SonicStark@users.noreply.github.com>
Co-authored-by: Xeonacid <h.dwwwwww@gmail.com>
Co-authored-by: Nils Bars <nils.bars@rub.de>
Co-authored-by: Jean-Romain Garnier <7504819+JRomainG@users.noreply.github.com>
Co-authored-by: Jean-Romain Garnier <jean-romain.garnier@airbus.com>
-rw-r--r--GNUmakefile.llvm6
-rwxr-xr-xafl-persistent-config8
-rwxr-xr-xafl-system-config1
-rw-r--r--docs/Changelog.md12
-rw-r--r--docs/custom_mutators.md2
-rw-r--r--include/envs.h319
-rw-r--r--qemu_mode/QEMUAFL_VERSION2
-rw-r--r--qemu_mode/README.md33
-rwxr-xr-xqemu_mode/build_qemu_support.sh4
m---------qemu_mode/qemuafl0
-rw-r--r--src/afl-cc.c3318
-rw-r--r--src/afl-fuzz-run.c41
-rw-r--r--src/afl-fuzz.c4
13 files changed, 2087 insertions, 1663 deletions
diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm
index c704d772..7437130d 100644
--- a/GNUmakefile.llvm
+++ b/GNUmakefile.llvm
@@ -51,7 +51,7 @@ LLVM_TOO_OLD = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^[1-9]
 LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[0-9]' && echo 1 || echo 0 )
 LLVM_NEWER_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[6-9]' && echo 1 || echo 0 )
 LLVM_13_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[3-9]' && echo 1 || echo 0 )
-LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]' && echo 1 || echo 0 )
+LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[2-9]' && echo 1 || echo 0 )
 LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)
 LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
 LLVM_STDCXX = gnu++11
@@ -95,12 +95,12 @@ ifeq "$(LLVM_NEWER_API)" "1"
 endif
 
 ifeq "$(LLVM_HAVE_LTO)" "1"
-  $(info [+] llvm_mode detected llvm 11+, enabling afl-lto LTO implementation)
+  $(info [+] llvm_mode detected llvm 12+, enabling afl-lto LTO implementation)
   LLVM_LTO = 1
 endif
 
 ifeq "$(LLVM_LTO)" "0"
-  $(info [+] llvm_mode detected llvm < 11, afl-lto LTO will not be build.)
+  $(info [+] llvm_mode detected llvm < 12, afl-lto LTO will not be build.)
 endif
 
 ifeq "$(LLVM_APPLE_XCODE)" "1"
diff --git a/afl-persistent-config b/afl-persistent-config
index d78db286..26be9d9f 100755
--- a/afl-persistent-config
+++ b/afl-persistent-config
@@ -38,6 +38,7 @@ fi
 
 echo
 PLATFORM=`uname -s`
+ARCH=`uname -m`
 
 # check that we're on Mac
 if [[ "$PLATFORM" = "Darwin" ]] ; then
@@ -87,6 +88,13 @@ if [[ "$PLATFORM" = "Darwin" ]] ; then
 </plist>
 EOF
 
+  if [[ "$ARCH" = "x86_64" ]]; then
+    echo "Disabling ASLR system wide"
+    nvram boot-args="no_aslr=1"
+  else
+    echo NOTICE: on ARM64 we do not know currently how to disable system wide ASLR, please report if you know how.
+  fi
+
   echo
   echo "Reboot and enjoy your fuzzing"
   exit 0
diff --git a/afl-system-config b/afl-system-config
index c633e4e8..7e2cb688 100755
--- a/afl-system-config
+++ b/afl-system-config
@@ -25,6 +25,7 @@ echo "WARNING: this reduces the security of the system!"
 echo
 if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
 	echo "Warning: you need to be root to run this!"
+	sleep 1
 	# we do not exit as other mechanisms exist that allows to do this than
 	# being root. let the errors speak for themselves.
 fi
diff --git a/docs/Changelog.md b/docs/Changelog.md
index adc81d64..c681c4e1 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -9,15 +9,23 @@
       explore is slightly better now.
     - fixed minor issues in the mutation engine, thanks to @futhewo for
       reporting!
+  - afl-cc:
+    - large rewrite by @SonicStark which fixes a few corner cases, thanks!
+    - LTO mode now requires llvm 12+
   - instrumentation:
     - LLVM 18 support, thanks to @devnexen!
     - Injection (SQL, LDAP, XSS) feature now available, see
       `instrumentation/README.injections.md` how to activate/use/expand.
     - compcov/LAF-intel:
       - floating point splitting bug fix by @hexcoder
-      - due a bug in LLVM 17 integer splitting is disabled!
+      - due a bug in LLVM 17 integer splitting is disabled there!
       - when splitting floats was selected, integers were always split as well,
-        fixed to require AFL_LLVM_LAF_SPLIT_COMPARES as it should
+        fixed to require AFL_LLVM_LAF_SPLIT_COMPARES or _ALL as it should
+  - qemu_mode:
+    - plugins are now activated by default and a new module is included that
+      produces drcov compatible traces for lighthouse/lightkeeper/...
+      thanks to @JRomainG to submitting!
+  - updated the custom grammar mutator
 
 
 ### Version ++4.09c (release)
diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md
index ce0a42dc..73e3c802 100644
--- a/docs/custom_mutators.md
+++ b/docs/custom_mutators.md
@@ -73,7 +73,7 @@ def init(seed):
 def fuzz_count(buf):
     return cnt
 
-def splice_optout()
+def splice_optout():
     pass
 
 def fuzz(buf, add_buf, max_size):
diff --git a/include/envs.h b/include/envs.h
index aa5c658e..0f645d23 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -16,255 +16,104 @@ static char *afl_environment_deprecated[] = {
 
 static char *afl_environment_variables[] = {
 
-    "AFL_ALIGNED_ALLOC",
-    "AFL_ALLOW_TMP",
-    "AFL_ANALYZE_HEX",
-    "AFL_AS",
-    "AFL_AUTORESUME",
-    "AFL_AS_FORCE_INSTRUMENT",
-    "AFL_BENCH_JUST_ONE",
-    "AFL_BENCH_UNTIL_CRASH",
-    "AFL_CAL_FAST",
-    "AFL_CC",
-    "AFL_CC_COMPILER",
-    "AFL_CMIN_ALLOW_ANY",
-    "AFL_CMIN_CRASHES_ONLY",
-    "AFL_CMPLOG_ONLY_NEW",
-    "AFL_CODE_END",
-    "AFL_CODE_START",
-    "AFL_COMPCOV_BINNAME",
-    "AFL_COMPCOV_LEVEL",
-    "AFL_CRASH_EXITCODE",
-    "AFL_CRASHING_SEEDS_AS_NEW_CRASH",
-    "AFL_CUSTOM_MUTATOR_LIBRARY",
-    "AFL_CUSTOM_MUTATOR_ONLY",
-    "AFL_CUSTOM_INFO_PROGRAM",
-    "AFL_CUSTOM_INFO_PROGRAM_ARGV",
-    "AFL_CUSTOM_INFO_PROGRAM_INPUT",
-    "AFL_CUSTOM_INFO_OUT",
-    "AFL_CXX",
-    "AFL_CYCLE_SCHEDULES",
-    "AFL_DEBUG",
-    "AFL_DEBUG_CHILD",
-    "AFL_DEBUG_GDB",
-    "AFL_DEBUG_UNICORN",
-    "AFL_DISABLE_TRIM",
-    "AFL_DISABLE_LLVM_INSTRUMENTATION",
-    "AFL_DONT_OPTIMIZE",
-    "AFL_DRIVER_STDERR_DUPLICATE_FILENAME",
-    "AFL_DUMB_FORKSRV",
-    "AFL_EARLY_FORKSERVER",
-    "AFL_ENTRYPOINT",
-    "AFL_EXIT_WHEN_DONE",
-    "AFL_EXIT_ON_TIME",
-    "AFL_EXIT_ON_SEED_ISSUES",
-    "AFL_FAST_CAL",
-    "AFL_FINAL_SYNC",
-    "AFL_FORCE_UI",
-    "AFL_FRIDA_DEBUG_MAPS",
-    "AFL_FRIDA_DRIVER_NO_HOOK",
-    "AFL_FRIDA_EXCLUDE_RANGES",
-    "AFL_FRIDA_INST_CACHE_SIZE",
-    "AFL_FRIDA_INST_COVERAGE_ABSOLUTE",
-    "AFL_FRIDA_INST_COVERAGE_FILE",
-    "AFL_FRIDA_INST_DEBUG_FILE",
-    "AFL_FRIDA_INST_INSN",
-    "AFL_FRIDA_INST_JIT",
-    "AFL_FRIDA_INST_NO_CACHE",
-    "AFL_FRIDA_INST_NO_DYNAMIC_LOAD",
-    "AFL_FRIDA_INST_NO_OPTIMIZE",
-    "AFL_FRIDA_INST_NO_PREFETCH",
-    "AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH",
+    "AFL_ALIGNED_ALLOC", "AFL_ALLOW_TMP", "AFL_ANALYZE_HEX", "AFL_AS",
+    "AFL_AUTORESUME", "AFL_AS_FORCE_INSTRUMENT", "AFL_BENCH_JUST_ONE",
+    "AFL_BENCH_UNTIL_CRASH", "AFL_CAL_FAST", "AFL_CC", "AFL_CC_COMPILER",
+    "AFL_CMIN_ALLOW_ANY", "AFL_CMIN_CRASHES_ONLY", "AFL_CMPLOG_ONLY_NEW",
+    "AFL_CODE_END", "AFL_CODE_START", "AFL_COMPCOV_BINNAME",
+    "AFL_COMPCOV_LEVEL", "AFL_CRASH_EXITCODE",
+    "AFL_CRASHING_SEEDS_AS_NEW_CRASH", "AFL_CUSTOM_MUTATOR_LIBRARY",
+    "AFL_CUSTOM_MUTATOR_ONLY", "AFL_CUSTOM_INFO_PROGRAM",
+    "AFL_CUSTOM_INFO_PROGRAM_ARGV", "AFL_CUSTOM_INFO_PROGRAM_INPUT",
+    "AFL_CUSTOM_INFO_OUT", "AFL_CXX", "AFL_CYCLE_SCHEDULES", "AFL_DEBUG",
+    "AFL_DEBUG_CHILD", "AFL_DEBUG_GDB", "AFL_DEBUG_UNICORN", "AFL_DISABLE_TRIM",
+    "AFL_DISABLE_LLVM_INSTRUMENTATION", "AFL_DONT_OPTIMIZE",
+    "AFL_DRIVER_STDERR_DUPLICATE_FILENAME", "AFL_DUMB_FORKSRV",
+    "AFL_EARLY_FORKSERVER", "AFL_ENTRYPOINT", "AFL_EXIT_WHEN_DONE",
+    "AFL_EXIT_ON_TIME", "AFL_EXIT_ON_SEED_ISSUES", "AFL_FAST_CAL",
+    "AFL_FINAL_SYNC", "AFL_FORCE_UI", "AFL_FRIDA_DEBUG_MAPS",
+    "AFL_FRIDA_DRIVER_NO_HOOK", "AFL_FRIDA_EXCLUDE_RANGES",
+    "AFL_FRIDA_INST_CACHE_SIZE", "AFL_FRIDA_INST_COVERAGE_ABSOLUTE",
+    "AFL_FRIDA_INST_COVERAGE_FILE", "AFL_FRIDA_INST_DEBUG_FILE",
+    "AFL_FRIDA_INST_INSN", "AFL_FRIDA_INST_JIT", "AFL_FRIDA_INST_NO_CACHE",
+    "AFL_FRIDA_INST_NO_DYNAMIC_LOAD", "AFL_FRIDA_INST_NO_OPTIMIZE",
+    "AFL_FRIDA_INST_NO_PREFETCH", "AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH",
     "AFL_FRIDA_INST_NO_SUPPRESS"
     "AFL_FRIDA_INST_RANGES",
-    "AFL_FRIDA_INST_REGS_FILE",
-    "AFL_FRIDA_INST_SEED",
-    "AFL_FRIDA_INST_TRACE",
-    "AFL_FRIDA_INST_TRACE_UNIQUE",
-    "AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE",
-    "AFL_FRIDA_JS_SCRIPT",
-    "AFL_FRIDA_OUTPUT_STDOUT",
-    "AFL_FRIDA_OUTPUT_STDERR",
-    "AFL_FRIDA_PERSISTENT_ADDR",
-    "AFL_FRIDA_PERSISTENT_CNT",
-    "AFL_FRIDA_PERSISTENT_DEBUG",
-    "AFL_FRIDA_PERSISTENT_HOOK",
-    "AFL_FRIDA_PERSISTENT_RET",
-    "AFL_FRIDA_STALKER_ADJACENT_BLOCKS",
-    "AFL_FRIDA_STALKER_IC_ENTRIES",
-    "AFL_FRIDA_STALKER_NO_BACKPATCH",
-    "AFL_FRIDA_STATS_FILE",
-    "AFL_FRIDA_STATS_INTERVAL",
-    "AFL_FRIDA_TRACEABLE",
+    "AFL_FRIDA_INST_REGS_FILE", "AFL_FRIDA_INST_SEED", "AFL_FRIDA_INST_TRACE",
+    "AFL_FRIDA_INST_TRACE_UNIQUE", "AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE",
+    "AFL_FRIDA_JS_SCRIPT", "AFL_FRIDA_OUTPUT_STDOUT", "AFL_FRIDA_OUTPUT_STDERR",
+    "AFL_FRIDA_PERSISTENT_ADDR", "AFL_FRIDA_PERSISTENT_CNT",
+    "AFL_FRIDA_PERSISTENT_DEBUG", "AFL_FRIDA_PERSISTENT_HOOK",
+    "AFL_FRIDA_PERSISTENT_RET", "AFL_FRIDA_STALKER_ADJACENT_BLOCKS",
+    "AFL_FRIDA_STALKER_IC_ENTRIES", "AFL_FRIDA_STALKER_NO_BACKPATCH",
+    "AFL_FRIDA_STATS_FILE", "AFL_FRIDA_STATS_INTERVAL", "AFL_FRIDA_TRACEABLE",
     "AFL_FRIDA_VERBOSE",
     "AFL_FUZZER_ARGS",  // oss-fuzz
-    "AFL_FUZZER_STATS_UPDATE_INTERVAL",
-    "AFL_GDB",
-    "AFL_GCC_ALLOWLIST",
-    "AFL_GCC_DENYLIST",
-    "AFL_GCC_BLOCKLIST",
-    "AFL_GCC_INSTRUMENT_FILE",
-    "AFL_GCC_OUT_OF_LINE",
-    "AFL_GCC_SKIP_NEVERZERO",
-    "AFL_GCJ",
-    "AFL_HANG_TMOUT",
-    "AFL_FORKSRV_INIT_TMOUT",
-    "AFL_HARDEN",
-    "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES",
-    "AFL_IGNORE_PROBLEMS",
-    "AFL_IGNORE_PROBLEMS_COVERAGE",
-    "AFL_IGNORE_SEED_PROBLEMS",
-    "AFL_IGNORE_TIMEOUTS",
-    "AFL_IGNORE_UNKNOWN_ENVS",
-    "AFL_IMPORT_FIRST",
-    "AFL_INPUT_LEN_MIN",
-    "AFL_INPUT_LEN_MAX",
-    "AFL_INST_LIBS",
-    "AFL_INST_RATIO",
-    "AFL_KEEP_TIMEOUTS",
-    "AFL_KILL_SIGNAL",
-    "AFL_FORK_SERVER_KILL_SIGNAL",
-    "AFL_KEEP_TRACES",
-    "AFL_KEEP_ASSEMBLY",
-    "AFL_LD_HARD_FAIL",
-    "AFL_LD_LIMIT_MB",
-    "AFL_LD_NO_CALLOC_OVER",
-    "AFL_LD_PASSTHROUGH",
-    "AFL_REAL_LD",
-    "AFL_LD_PRELOAD",
-    "AFL_LD_VERBOSE",
-    "AFL_LLVM_ALLOWLIST",
-    "AFL_LLVM_DENYLIST",
-    "AFL_LLVM_BLOCKLIST",
-    "AFL_CMPLOG",
-    "AFL_LLVM_CMPLOG",
-    "AFL_GCC_CMPLOG",
-    "AFL_LLVM_INSTRIM",
-    "AFL_LLVM_CALLER",
-    "AFL_LLVM_CTX",
-    "AFL_LLVM_CTX_K",
-    "AFL_LLVM_DICT2FILE",
-    "AFL_LLVM_DICT2FILE_NO_MAIN",
-    "AFL_LLVM_DOCUMENT_IDS",
-    "AFL_LLVM_INSTRIM_LOOPHEAD",
-    "AFL_LLVM_INSTRUMENT",
-    "AFL_LLVM_LTO_AUTODICTIONARY",
-    "AFL_LLVM_AUTODICTIONARY",
+    "AFL_FUZZER_STATS_UPDATE_INTERVAL", "AFL_GDB", "AFL_GCC_ALLOWLIST",
+    "AFL_GCC_DENYLIST", "AFL_GCC_BLOCKLIST", "AFL_GCC_INSTRUMENT_FILE",
+    "AFL_GCC_OUT_OF_LINE", "AFL_GCC_SKIP_NEVERZERO", "AFL_GCJ",
+    "AFL_HANG_TMOUT", "AFL_FORKSRV_INIT_TMOUT", "AFL_HARDEN",
+    "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", "AFL_IGNORE_PROBLEMS",
+    "AFL_IGNORE_PROBLEMS_COVERAGE", "AFL_IGNORE_SEED_PROBLEMS",
+    "AFL_IGNORE_TIMEOUTS", "AFL_IGNORE_UNKNOWN_ENVS", "AFL_IMPORT_FIRST",
+    "AFL_INPUT_LEN_MIN", "AFL_INPUT_LEN_MAX", "AFL_INST_LIBS", "AFL_INST_RATIO",
+    "AFL_KEEP_TIMEOUTS", "AFL_KILL_SIGNAL", "AFL_FORK_SERVER_KILL_SIGNAL",
+    "AFL_KEEP_TRACES", "AFL_KEEP_ASSEMBLY", "AFL_LD_HARD_FAIL",
+    "AFL_LD_LIMIT_MB", "AFL_LD_NO_CALLOC_OVER", "AFL_LD_PASSTHROUGH",
+    "AFL_REAL_LD", "AFL_LD_PRELOAD", "AFL_LD_VERBOSE", "AFL_LLVM_ALLOWLIST",
+    "AFL_LLVM_DENYLIST", "AFL_LLVM_BLOCKLIST", "AFL_CMPLOG", "AFL_LLVM_CMPLOG",
+    "AFL_GCC_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CALLER", "AFL_LLVM_CTX",
+    "AFL_LLVM_CTX_K", "AFL_LLVM_DICT2FILE", "AFL_LLVM_DICT2FILE_NO_MAIN",
+    "AFL_LLVM_DOCUMENT_IDS", "AFL_LLVM_INSTRIM_LOOPHEAD", "AFL_LLVM_INSTRUMENT",
+    "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY",
     "AFL_LLVM_SKIPSINGLEBLOCK",
     // Marker: ADD_TO_INJECTIONS
-    "AFL_LLVM_INJECTIONS_ALL",
-    "AFL_LLVM_INJECTIONS_SQL",
-    "AFL_LLVM_INJECTIONS_LDAP",
-    "AFL_LLVM_INJECTIONS_XSS",
-    "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK",
-    "AFL_LLVM_LAF_SPLIT_COMPARES",
-    "AFL_LLVM_LAF_SPLIT_COMPARES_BITW",
-    "AFL_LLVM_LAF_SPLIT_FLOATS",
-    "AFL_LLVM_LAF_SPLIT_SWITCHES",
-    "AFL_LLVM_LAF_ALL",
-    "AFL_LLVM_LAF_TRANSFORM_COMPARES",
-    "AFL_LLVM_MAP_ADDR",
-    "AFL_LLVM_MAP_DYNAMIC",
-    "AFL_LLVM_NGRAM_SIZE",
-    "AFL_NGRAM_SIZE",
-    "AFL_LLVM_NO_RPATH",
-    "AFL_LLVM_NOT_ZERO",
-    "AFL_LLVM_INSTRUMENT_FILE",
-    "AFL_LLVM_THREADSAFE_INST",
-    "AFL_LLVM_SKIP_NEVERZERO",
-    "AFL_NO_AFFINITY",
-    "AFL_TRY_AFFINITY",
-    "AFL_LLVM_LTO_DONTWRITEID",
+    "AFL_LLVM_INJECTIONS_ALL", "AFL_LLVM_INJECTIONS_SQL",
+    "AFL_LLVM_INJECTIONS_LDAP", "AFL_LLVM_INJECTIONS_XSS",
+    "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", "AFL_LLVM_LAF_SPLIT_COMPARES",
+    "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", "AFL_LLVM_LAF_SPLIT_FLOATS",
+    "AFL_LLVM_LAF_SPLIT_SWITCHES", "AFL_LLVM_LAF_ALL",
+    "AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_MAP_ADDR",
+    "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", "AFL_NGRAM_SIZE",
+    "AFL_LLVM_NO_RPATH", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_INSTRUMENT_FILE",
+    "AFL_LLVM_THREADSAFE_INST", "AFL_LLVM_SKIP_NEVERZERO", "AFL_NO_AFFINITY",
+    "AFL_TRY_AFFINITY", "AFL_LLVM_LTO_DONTWRITEID",
     "AFL_LLVM_LTO_SKIPINIT"
     "AFL_LLVM_LTO_STARTID",
-    "AFL_FUZZER_LOOPCOUNT",
-    "AFL_NO_ARITH",
-    "AFL_NO_AUTODICT",
-    "AFL_NO_BUILTIN",
+    "AFL_FUZZER_LOOPCOUNT", "AFL_NO_ARITH", "AFL_NO_AUTODICT", "AFL_NO_BUILTIN",
 #if defined USE_COLOR && !defined ALWAYS_COLORED
-    "AFL_NO_COLOR",
-    "AFL_NO_COLOUR",
+    "AFL_NO_COLOR", "AFL_NO_COLOUR",
 #endif
     "AFL_NO_CPU_RED",
     "AFL_NO_CFG_FUZZING",  // afl.rs rust crate option
-    "AFL_NO_CRASH_README",
-    "AFL_NO_FORKSRV",
-    "AFL_NO_UI",
-    "AFL_NO_PYTHON",
-    "AFL_NO_STARTUP_CALIBRATION",
-    "AFL_NO_WARN_INSTABILITY",
-    "AFL_UNTRACER_FILE",
-    "AFL_LLVM_USE_TRACE_PC",
-    "AFL_MAP_SIZE",
-    "AFL_MAPSIZE",
+    "AFL_NO_CRASH_README", "AFL_NO_FORKSRV", "AFL_NO_UI", "AFL_NO_PYTHON",
+    "AFL_NO_STARTUP_CALIBRATION", "AFL_NO_WARN_INSTABILITY",
+    "AFL_UNTRACER_FILE", "AFL_LLVM_USE_TRACE_PC", "AFL_MAP_SIZE", "AFL_MAPSIZE",
     "AFL_MAX_DET_EXTRAS",
     "AFL_NO_X86",  // not really an env but we dont want to warn on it
-    "AFL_NOOPT",
-    "AFL_NYX_AUX_SIZE",
-    "AFL_NYX_DISABLE_SNAPSHOT_MODE",
-    "AFL_NYX_LOG",
-    "AFL_NYX_REUSE_SNAPSHOT",
-    "AFL_PASSTHROUGH",
-    "AFL_PATH",
-    "AFL_PERFORMANCE_FILE",
-    "AFL_PERSISTENT_RECORD",
-    "AFL_POST_PROCESS_KEEP_ORIGINAL",
-    "AFL_PRELOAD",
-    "AFL_TARGET_ENV",
-    "AFL_PYTHON_MODULE",
-    "AFL_QEMU_CUSTOM_BIN",
-    "AFL_QEMU_COMPCOV",
-    "AFL_QEMU_COMPCOV_DEBUG",
-    "AFL_QEMU_DEBUG_MAPS",
-    "AFL_QEMU_DISABLE_CACHE",
-    "AFL_QEMU_DRIVER_NO_HOOK",
-    "AFL_QEMU_FORCE_DFL",
-    "AFL_QEMU_PERSISTENT_ADDR",
-    "AFL_QEMU_PERSISTENT_CNT",
-    "AFL_QEMU_PERSISTENT_GPR",
-    "AFL_QEMU_PERSISTENT_HOOK",
-    "AFL_QEMU_PERSISTENT_MEM",
-    "AFL_QEMU_PERSISTENT_RET",
-    "AFL_QEMU_PERSISTENT_RETADDR_OFFSET",
-    "AFL_QEMU_PERSISTENT_EXITS",
-    "AFL_QEMU_INST_RANGES",
-    "AFL_QEMU_EXCLUDE_RANGES",
-    "AFL_QEMU_SNAPSHOT",
-    "AFL_QEMU_TRACK_UNSTABLE",
-    "AFL_QUIET",
-    "AFL_RANDOM_ALLOC_CANARY",
-    "AFL_REAL_PATH",
-    "AFL_SHUFFLE_QUEUE",
-    "AFL_SKIP_BIN_CHECK",
-    "AFL_SKIP_CPUFREQ",
-    "AFL_SKIP_CRASHES",
-    "AFL_SKIP_OSSFUZZ",
-    "AFL_STATSD",
-    "AFL_STATSD_HOST",
-    "AFL_STATSD_PORT",
-    "AFL_STATSD_TAGS_FLAVOR",
-    "AFL_SYNC_TIME",
-    "AFL_TESTCACHE_SIZE",
-    "AFL_TESTCACHE_ENTRIES",
-    "AFL_TMIN_EXACT",
-    "AFL_TMPDIR",
-    "AFL_TOKEN_FILE",
-    "AFL_TRACE_PC",
-    "AFL_USE_ASAN",
-    "AFL_USE_MSAN",
-    "AFL_USE_TRACE_PC",
-    "AFL_USE_UBSAN",
-    "AFL_USE_TSAN",
-    "AFL_USE_CFISAN",
-    "AFL_USE_LSAN",
-    "AFL_WINE_PATH",
-    "AFL_NO_SNAPSHOT",
-    "AFL_EXPAND_HAVOC_NOW",
-    "AFL_USE_FASAN",
-    "AFL_USE_QASAN",
-    "AFL_PRINT_FILENAMES",
-    "AFL_PIZZA_MODE",
-    NULL
+    "AFL_NOOPT", "AFL_NYX_AUX_SIZE", "AFL_NYX_DISABLE_SNAPSHOT_MODE",
+    "AFL_NYX_LOG", "AFL_NYX_REUSE_SNAPSHOT", "AFL_PASSTHROUGH", "AFL_PATH",
+    "AFL_PERFORMANCE_FILE", "AFL_PERSISTENT_RECORD",
+    "AFL_POST_PROCESS_KEEP_ORIGINAL", "AFL_PRELOAD", "AFL_TARGET_ENV",
+    "AFL_PYTHON_MODULE", "AFL_QEMU_CUSTOM_BIN", "AFL_QEMU_COMPCOV",
+    "AFL_QEMU_COMPCOV_DEBUG", "AFL_QEMU_DEBUG_MAPS", "AFL_QEMU_DISABLE_CACHE",
+    "AFL_QEMU_DRIVER_NO_HOOK", "AFL_QEMU_FORCE_DFL", "AFL_QEMU_PERSISTENT_ADDR",
+    "AFL_QEMU_PERSISTENT_CNT", "AFL_QEMU_PERSISTENT_GPR",
+    "AFL_QEMU_PERSISTENT_HOOK", "AFL_QEMU_PERSISTENT_MEM",
+    "AFL_QEMU_PERSISTENT_RET", "AFL_QEMU_PERSISTENT_RETADDR_OFFSET",
+    "AFL_QEMU_PERSISTENT_EXITS", "AFL_QEMU_INST_RANGES",
+    "AFL_QEMU_EXCLUDE_RANGES", "AFL_QEMU_SNAPSHOT", "AFL_QEMU_TRACK_UNSTABLE",
+    "AFL_QUIET", "AFL_RANDOM_ALLOC_CANARY", "AFL_REAL_PATH",
+    "AFL_SHUFFLE_QUEUE", "AFL_SKIP_BIN_CHECK", "AFL_SKIP_CPUFREQ",
+    "AFL_SKIP_CRASHES", "AFL_SKIP_OSSFUZZ", "AFL_STATSD", "AFL_STATSD_HOST",
+    "AFL_STATSD_PORT", "AFL_STATSD_TAGS_FLAVOR", "AFL_SYNC_TIME",
+    "AFL_TESTCACHE_SIZE", "AFL_TESTCACHE_ENTRIES", "AFL_TMIN_EXACT",
+    "AFL_TMPDIR", "AFL_TOKEN_FILE", "AFL_TRACE_PC", "AFL_USE_ASAN",
+    "AFL_USE_MSAN", "AFL_USE_TRACE_PC", "AFL_USE_UBSAN", "AFL_USE_TSAN",
+    "AFL_USE_CFISAN", "AFL_USE_LSAN", "AFL_WINE_PATH", "AFL_NO_SNAPSHOT",
+    "AFL_EXPAND_HAVOC_NOW", "AFL_USE_FASAN", "AFL_USE_QASAN",
+    "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", NULL
 
 };
 
diff --git a/qemu_mode/QEMUAFL_VERSION b/qemu_mode/QEMUAFL_VERSION
index 44ea5345..b4e764b7 100644
--- a/qemu_mode/QEMUAFL_VERSION
+++ b/qemu_mode/QEMUAFL_VERSION
@@ -1 +1 @@
-a1321713c7
+e63c9af193
diff --git a/qemu_mode/README.md b/qemu_mode/README.md
index 92038737..b78eb297 100644
--- a/qemu_mode/README.md
+++ b/qemu_mode/README.md
@@ -193,12 +193,39 @@ Comparative measurements of execution speed or instrumentation coverage will be
 fairly meaningless if the optimization levels or instrumentation scopes don't
 match.
 
-## 12) Other features
+## 12) Coverage information
+
+Coverage information about a run of a target binary can be obtained using a
+dedicated QEMU user mode plugin enabled at runtime: the `drcov.c` plugin
+collects coverage information from the target binary and writes it in the Drcov
+format. This file can then be loaded using tools such as
+[lighthouse](https://github.com/gaasedelen/lighthouse),
+[lightkeeper](https://github.com/WorksButNotTested/lightkeeper) or
+[Cartographer](https://github.com/nccgroup/Cartographer).
+
+To compile the QEMU TCG plugins, run the following command from the `qemuafl`
+directory:
+
+```
+make plugins
+```
+
+Plugins can be loaded using either the `QEMU_PLUGIN` environment variable or
+using the `-plugin` option. For example:
+
+```
+afl-qemu-trace -plugin qemuafl/build/contrib/plugins/libdrcov.so,arg=filename=/tmp/target.drcov.trace <target> <args>
+```
+
+This would execute the target binary with the provided arguments and, once done,
+would write coverage information at `/tmp/target.drcov.trace`.
+
+## 13) Other features
 
 With `AFL_QEMU_FORCE_DFL`, you force QEMU to ignore the registered signal
 handlers of the target.
 
-## 13) Gotchas, feedback, bugs
+## 14) Gotchas, feedback, bugs
 
 If you need to fix up checksums or do other cleanups on mutated test cases, see
 `afl_custom_post_process` in custom_mutators/examples/example.c for a viable
@@ -217,7 +244,7 @@ program may be utilizing. In particular, it does not appear to have full support
 for AVX2/FMA3. Using binaries for older CPUs or recompiling them with
 `-march=core2`, can help.
 
-## 14) Alternatives: static rewriting
+## 15) Alternatives: static rewriting
 
 Statically rewriting binaries just once, instead of attempting to translate them
 at run time, can be a faster alternative. That said, static rewriting is fraught
diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh
index f59cba78..3f8a88f2 100755
--- a/qemu_mode/build_qemu_support.sh
+++ b/qemu_mode/build_qemu_support.sh
@@ -132,7 +132,10 @@ 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 \
@@ -162,7 +165,6 @@ QEMU_CONF_FLAGS=" \
   --disable-numa \
   --disable-opengl \
   --disable-parallels \
-  --disable-plugins \
   --disable-qcow1 \
   --disable-qed \
   --disable-rbd \
diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl
-Subproject b0abbe2e74ed74ff6ff25b5ea3110d27ba97800
+Subproject e63c9af1937c13163cd1bc8bc276101441cbe70
diff --git a/src/afl-cc.c b/src/afl-cc.c
index 54c733c9..192c5423 100644
--- a/src/afl-cc.c
+++ b/src/afl-cc.c
@@ -47,23 +47,22 @@
   #define LLVM_MINOR 0
 #endif
 
-static u8  *obj_path;                  /* Path to runtime libraries         */
-static u8 **cc_params;                 /* Parameters passed to the real CC  */
-static u32  cc_par_cnt = 1;            /* Param count, including argv0      */
-static u8   clang_mode;                /* Invoked as afl-clang*?            */
-static u8   llvm_fullpath[PATH_MAX];
-static u8   instrument_mode, instrument_opt_mode, ngram_size, ctx_k, lto_mode;
-static u8   compiler_mode, plusplus_mode, have_instr_env = 0, need_aflpplib = 0;
-static u8   have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0;
-static u8  *lto_flag = AFL_CLANG_FLTO, *argvnull;
-static u8   debug;
-static u8   cwd[4096];
-static u8   cmplog_mode;
-u8          use_stdin;                                             /* dummy */
-static int  passthrough;
-// static u8 *march_opt = CFLAGS_OPT;
-
-enum {
+#ifndef MAX_PARAMS_NUM
+  #define MAX_PARAMS_NUM 2048
+#endif
+
+/* Global declarations */
+
+typedef enum {
+
+  PARAM_MISS,  // not matched
+  PARAM_SCAN,  // scan only
+  PARAM_KEEP,  // kept as-is
+  PARAM_DROP,  // ignored
+
+} param_st;
+
+typedef enum {
 
   INSTRUMENT_DEFAULT = 0,
   INSTRUMENT_CLASSIC = 1,
@@ -80,7 +79,20 @@ enum {
   INSTRUMENT_OPT_CTX_K = 64,
   INSTRUMENT_OPT_CODECOV = 128,
 
-};
+} instrument_mode_id;
+
+typedef enum {
+
+  UNSET = 0,
+  LTO = 1,
+  LLVM = 2,
+  GCC_PLUGIN = 3,
+  GCC = 4,
+  CLANG = 5
+
+} compiler_mode_id;
+
+static u8 cwd[4096];
 
 char instrument_mode_string[18][18] = {
 
@@ -105,17 +117,6 @@ char instrument_mode_string[18][18] = {
 
 };
 
-enum {
-
-  UNSET = 0,
-  LTO = 1,
-  LLVM = 2,
-  GCC_PLUGIN = 3,
-  GCC = 4,
-  CLANG = 5
-
-};
-
 char compiler_mode_string[7][12] = {
 
     "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN",
@@ -123,6 +124,18 @@ char compiler_mode_string[7][12] = {
 
 };
 
+u8 *instrument_mode_2str(instrument_mode_id i) {
+
+  return instrument_mode_string[i];
+
+}
+
+u8 *compiler_mode_2str(compiler_mode_id i) {
+
+  return compiler_mode_string[i];
+
+}
+
 u8 *getthecwd() {
 
   if (getcwd(cwd, sizeof(cwd)) == NULL) {
@@ -136,26 +149,228 @@ u8 *getthecwd() {
 
 }
 
-/* Try to find a specific runtime we need, returns NULL on fail. */
+typedef struct aflcc_state {
+
+  u8 **cc_params;                      /* Parameters passed to the real CC  */
+  u32  cc_par_cnt;                     /* Param count, including argv0      */
+
+  u8 *argv0;                           /* Original argv0 (by strdup)        */
+  u8 *callname;                        /* Executable file argv0 indicated   */
+
+  u8 debug;
+
+  u8 compiler_mode, plusplus_mode, lto_mode;
+
+  u8 *lto_flag;
+
+  u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k;
+
+  u8 cmplog_mode;
+
+  u8 have_instr_env, have_gcc, have_clang, have_llvm, have_gcc_plugin, have_lto,
+      have_optimized_pcguard, have_instr_list;
+
+  u8 fortify_set, asan_set, x_set, bit_mode, preprocessor_only, have_unroll,
+      have_o, have_pic, have_c, shared_linking, partial_linking, non_dash;
+
+  // u8 *march_opt;
+  u8  need_aflpplib;
+  int passthrough;
+
+  u8  use_stdin;                                                   /* dummy */
+  u8 *argvnull;                                                    /* dummy */
+
+} aflcc_state_t;
+
+void aflcc_state_init(aflcc_state_t *, u8 *argv0);
+
+/* Try to find a specific runtime we need, the path to obj would be
+   allocated and returned. Otherwise it returns NULL on fail. */
+u8 *find_object(aflcc_state_t *, u8 *obj);
+
+void find_built_deps(aflcc_state_t *);
+
+static inline void limit_params(aflcc_state_t *aflcc, u32 add) {
+
+  if (aflcc->cc_par_cnt + add >= MAX_PARAMS_NUM)
+    FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM.");
+
+}
+
+static inline void insert_param(aflcc_state_t *aflcc, u8 *param) {
+
+  aflcc->cc_params[aflcc->cc_par_cnt++] = param;
+
+}
+
+static inline void insert_object(aflcc_state_t *aflcc, u8 *obj, u8 *fmt,
+                                 u8 *msg) {
+
+  u8 *_obj_path = find_object(aflcc, obj);
+  if (!_obj_path) {
+
+    if (msg)
+      FATAL("%s", msg);
+    else
+      FATAL("Unable to find '%s'", obj);
+
+  } else {
+
+    if (fmt) {
+
+      u8 *_obj_path_fmt = alloc_printf(fmt, _obj_path);
+      ck_free(_obj_path);
+      aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path_fmt;
+
+    } else {
+
+      aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path;
+
+    }
+
+  }
+
+}
+
+static inline void load_llvm_pass(aflcc_state_t *aflcc, u8 *pass) {
+
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+  #if LLVM_MAJOR < 16
+  insert_param(aflcc, "-fexperimental-new-pass-manager");
+  #endif
+  insert_object(aflcc, pass, "-fpass-plugin=%s", 0);
+#else
+  insert_param(aflcc, "-Xclang");
+  insert_param(aflcc, "-load");
+  insert_param(aflcc, "-Xclang");
+  insert_object(aflcc, pass, 0, 0);
+#endif
+
+}
+
+static inline void debugf_args(int argc, char **argv) {
+
+  DEBUGF("cd '%s';", getthecwd());
+  for (int i = 0; i < argc; i++)
+    SAYF(" '%s'", argv[i]);
+  SAYF("\n");
+  fflush(stdout);
+  fflush(stderr);
+
+}
+
+void compiler_mode_by_callname(aflcc_state_t *);
+void compiler_mode_by_environ(aflcc_state_t *);
+void compiler_mode_by_cmdline(aflcc_state_t *, int argc, char **argv);
+void instrument_mode_by_environ(aflcc_state_t *);
+void mode_final_checkout(aflcc_state_t *, int argc, char **argv);
+void mode_notification(aflcc_state_t *);
+
+void add_real_argv0(aflcc_state_t *);
+
+void add_defs_common(aflcc_state_t *);
+void add_defs_selective_instr(aflcc_state_t *);
+void add_defs_persistent_mode(aflcc_state_t *);
+void add_defs_fortify(aflcc_state_t *, u8);
+void add_defs_lsan_ctrl(aflcc_state_t *);
+
+param_st parse_fsanitize(aflcc_state_t *, u8 *, u8);
+void     add_sanitizers(aflcc_state_t *, char **envp);
+void     add_optimized_pcguard(aflcc_state_t *);
+void     add_native_pcguard(aflcc_state_t *);
+
+void add_assembler(aflcc_state_t *);
+void add_gcc_plugin(aflcc_state_t *);
+
+param_st parse_misc_params(aflcc_state_t *, u8 *, u8);
+void     add_misc_params(aflcc_state_t *);
+
+param_st parse_linking_params(aflcc_state_t *, u8 *, u8, u8 *skip_next,
+                              char **argv);
+
+void add_lto_linker(aflcc_state_t *);
+void add_lto_passes(aflcc_state_t *);
+void add_runtime(aflcc_state_t *);
+
+/* Working state */
+
+void aflcc_state_init(aflcc_state_t *aflcc, u8 *argv0) {
+
+  // Default NULL/0 is a good start
+  memset(aflcc, 0, sizeof(aflcc_state_t));
+
+  aflcc->cc_params = ck_alloc(MAX_PARAMS_NUM * sizeof(u8 *));
+  aflcc->cc_par_cnt = 1;
+
+  aflcc->lto_flag = AFL_CLANG_FLTO;
+
+  // aflcc->march_opt = CFLAGS_OPT;
+
+  /* callname & if C++ mode */
+
+  aflcc->argv0 = ck_strdup(argv0);
+
+  char *cname = NULL;
+
+  if ((cname = strrchr(aflcc->argv0, '/')) != NULL) {
+
+    cname++;
+
+  } else {
+
+    cname = aflcc->argv0;
+
+  }
+
+  aflcc->callname = cname;
+
+  if (strlen(cname) > 2 && (strncmp(cname + strlen(cname) - 2, "++", 2) == 0 ||
+                            strstr(cname, "-g++") != NULL)) {
+
+    aflcc->plusplus_mode = 1;
+
+  }
+
+  /* debug */
+
+  if (getenv("AFL_DEBUG")) {
+
+    aflcc->debug = 1;
+    if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG");
+
+  } else if (getenv("AFL_QUIET")) {
+
+    be_quiet = 1;
+
+  }
+
+  if ((getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) && (!aflcc->debug)) {
+
+    be_quiet = 1;
+
+  }
+
+}
 
 /*
   in find_object() we look here:
 
-  1. if obj_path is already set we look there first
-  2. then we check the $AFL_PATH environment variable location if set
-  3. next we check argv[0] if it has path information and use it
+  1. firstly we check the $AFL_PATH environment variable location if set
+  2. next we check argv[0] if it has path information and use it
     a) we also check ../lib/afl
-  4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and
+  3. if 2. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and
      FreeBSD with procfs)
     a) and check here in ../lib/afl too
-  5. we look into the AFL_PATH define (usually /usr/local/lib/afl)
-  6. we finally try the current directory
+  4. we look into the AFL_PATH define (usually /usr/local/lib/afl)
+  5. we finally try the current directory
 
   if all these attempts fail - we return NULL and the caller has to decide
-  what to do.
+  what to do. Otherwise the path to obj would be allocated and returned.
 */
 
-static u8 *find_object(u8 *obj, u8 *argv0) {
+u8 *find_object(aflcc_state_t *aflcc, u8 *obj) {
+
+  u8 *argv0 = aflcc->argv0;
 
   u8 *afl_path = getenv("AFL_PATH");
   u8 *slash = NULL, *tmp;
@@ -164,14 +379,9 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
 
     tmp = alloc_printf("%s/%s", afl_path, obj);
 
-    if (debug) DEBUGF("Trying %s\n", tmp);
+    if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-    if (!access(tmp, R_OK)) {
-
-      obj_path = afl_path;
-      return tmp;
-
-    }
+    if (!access(tmp, R_OK)) { return tmp; }
 
     ck_free(tmp);
 
@@ -190,11 +400,11 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
 
       tmp = alloc_printf("%s/%s", dir, obj);
 
-      if (debug) DEBUGF("Trying %s\n", tmp);
+      if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
       if (!access(tmp, R_OK)) {
 
-        obj_path = dir;
+        ck_free(dir);
         return tmp;
 
       }
@@ -202,12 +412,10 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
       ck_free(tmp);
       tmp = alloc_printf("%s/../lib/afl/%s", dir, obj);
 
-      if (debug) DEBUGF("Trying %s\n", tmp);
+      if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
       if (!access(tmp, R_OK)) {
 
-        u8 *dir2 = alloc_printf("%s/../lib/afl", dir);
-        obj_path = dir2;
         ck_free(dir);
         return tmp;
 
@@ -247,26 +455,16 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
             *slash = 0;
             tmp = alloc_printf("%s/%s", exepath, obj);
 
-            if (!access(tmp, R_OK)) {
-
-              u8 *dir = alloc_printf("%s", exepath);
-              obj_path = dir;
-              return tmp;
-
-            }
+            if (!access(tmp, R_OK)) { return tmp; }
 
             ck_free(tmp);
             tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj);
 
-            if (debug) DEBUGF("Trying %s\n", tmp);
-
-            if (!access(tmp, R_OK)) {
+            if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-              u8 *dir = alloc_printf("%s/../lib/afl/", exepath);
-              obj_path = dir;
-              return tmp;
+            if (!access(tmp, R_OK)) { return tmp; }
 
-            }
+            ck_free(tmp);
 
           }
 
@@ -283,1844 +481,2037 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
 
   tmp = alloc_printf("%s/%s", AFL_PATH, obj);
 
-  if (debug) DEBUGF("Trying %s\n", tmp);
-
-  if (!access(tmp, R_OK)) {
-
-    obj_path = AFL_PATH;
-    return tmp;
+  if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-  }
+  if (!access(tmp, R_OK)) { return tmp; }
 
   ck_free(tmp);
-
   tmp = alloc_printf("./%s", obj);
 
-  if (debug) DEBUGF("Trying %s\n", tmp);
-
-  if (!access(tmp, R_OK)) {
+  if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-    obj_path = ".";
-    return tmp;
-
-  }
+  if (!access(tmp, R_OK)) { return tmp; }
 
   ck_free(tmp);
 
-  if (debug) DEBUGF("Trying ... giving up\n");
+  if (aflcc->debug) DEBUGF("Trying ... giving up\n");
 
   return NULL;
 
 }
 
-void parse_fsanitize(char *string) {
+void find_built_deps(aflcc_state_t *aflcc) {
 
-  char *p, *ptr = string + strlen("-fsanitize=");
-  char *new = malloc(strlen(string) + 1);
-  char *tmp = malloc(strlen(ptr) + 1);
-  u32   count = 0, len, ende = 0;
+  char *ptr = NULL;
 
-  if (!new || !tmp) { FATAL("could not acquire memory"); }
-  strcpy(new, "-fsanitize=");
+#if defined(__x86_64__)
+  if ((ptr = find_object(aflcc, "as")) != NULL) {
 
-  do {
+  #ifndef __APPLE__
+    // on OSX clang masquerades as GCC
+    aflcc->have_gcc = 1;
+  #endif
+    aflcc->have_clang = 1;
+    ck_free(ptr);
 
-    p = strchr(ptr, ',');
-    if (!p) {
+  }
 
-      p = ptr + strlen(ptr) + 1;
-      ende = 1;
+#endif
 
-    }
+  if ((ptr = find_object(aflcc, "SanitizerCoveragePCGUARD.so")) != NULL) {
 
-    len = p - ptr;
-    if (len) {
+    aflcc->have_optimized_pcguard = 1;
+    ck_free(ptr);
 
-      strncpy(tmp, ptr, len);
-      tmp[len] = 0;
-      // fprintf(stderr, "Found: %s\n", tmp);
-      ptr += len + 1;
-      if (*tmp) {
+  }
 
-        u32 copy = 1;
-        if (!strcmp(tmp, "fuzzer")) {
+#if (LLVM_MAJOR >= 3)
 
-          need_aflpplib = 1;
-          copy = 0;
+  if ((ptr = find_object(aflcc, "SanitizerCoverageLTO.so")) != NULL) {
 
-        } else if (!strncmp(tmp, "fuzzer", 6)) {
+    aflcc->have_lto = 1;
+    ck_free(ptr);
 
-          copy = 0;
+  }
 
-        }
+  if ((ptr = find_object(aflcc, "cmplog-routines-pass.so")) != NULL) {
 
-        if (copy) {
+    aflcc->have_llvm = 1;
+    ck_free(ptr);
 
-          if (count) { strcat(new, ","); }
-          strcat(new, tmp);
-          ++count;
+  }
 
-        }
+#endif
 
-      }
+#ifdef __ANDROID__
+  aflcc->have_llvm = 1;
+#endif
 
-    } else {
+  if ((ptr = find_object(aflcc, "afl-gcc-pass.so")) != NULL) {
 
-      ptr++;                                    /*fprintf(stderr, "NO!\n"); */
+    aflcc->have_gcc_plugin = 1;
+    ck_free(ptr);
 
-    }
+  }
 
-  } while (!ende);
+#if !defined(__ANDROID__) && !defined(ANDROID)
+  ptr = find_object(aflcc, "afl-compiler-rt.o");
 
-  strcpy(string, new);
-  // fprintf(stderr, "string: %s\n", string);
-  // fprintf(stderr, "new: %s\n", new);
+  if (!ptr) {
 
-}
+    FATAL(
+        "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH "
+        "environment variable.");
 
-static u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0,
-          shared_linking = 0, preprocessor_only = 0, have_unroll = 0,
-          have_o = 0, have_pic = 0, have_c = 0, partial_linking = 0,
-          non_dash = 0;
+  }
 
-#ifndef MAX_PARAMS_NUM
-  #define MAX_PARAMS_NUM 2048
-#endif
+  if (aflcc->debug) { DEBUGF("rt=%s\n", ptr); }
 
-static void process_params(u32 argc, char **argv) {
+  ck_free(ptr);
+#endif
 
-  if (cc_par_cnt + argc >= MAX_PARAMS_NUM) {
+}
 
-    FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM.");
+/* compiler_mode & instrument_mode selecting */
 
-  }
+void compiler_mode_by_callname(aflcc_state_t *aflcc) {
 
-  // reset
-  have_instr_list = 0;
-  have_c = 0;
+  if (strncmp(aflcc->callname, "afl-clang-fast", 14) == 0) {
 
-  if (lto_mode && argc > 1) {
+    /* afl-clang-fast is always created there by makefile
+      just like afl-clang, burdened with special purposes:
+      - If llvm-config is not available (i.e. LLVM_MAJOR is 0),
+        or too old, it falls back to LLVM-NATIVE mode and let
+        the actual compiler complain if doesn't work.
+      - Otherwise try default llvm instruments except LTO.
+    */
+#if (LLVM_MAJOR >= 3)
+    aflcc->compiler_mode = LLVM;
+#else
+    aflcc->compiler_mode = CLANG;
+#endif
 
-    u32 idx;
-    for (idx = 1; idx < argc; idx++) {
+  } else
 
-      if (!strncasecmp(argv[idx], "-fpic", 5)) { have_pic = 1; }
+#if (LLVM_MAJOR >= 3)
 
-    }
+      if (strncmp(aflcc->callname, "afl-clang-lto", 13) == 0 ||
 
-  }
+          strncmp(aflcc->callname, "afl-lto", 7) == 0) {
 
-  // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]);
+    aflcc->compiler_mode = LTO;
 
-  /* Process the argument list. */
+  } else
 
-  u8 skip_next = 0;
-  while (--argc) {
+#endif
 
-    u8 *cur = *(++argv);
+      if (strncmp(aflcc->callname, "afl-gcc-fast", 12) == 0 ||
 
-    if (skip_next) {
+          strncmp(aflcc->callname, "afl-g++-fast", 12) == 0) {
 
-      skip_next = 0;
-      continue;
+    aflcc->compiler_mode = GCC_PLUGIN;
 
-    }
+#if defined(__x86_64__)
 
-    if (cur[0] != '-') { non_dash = 1; }
-    if (!strncmp(cur, "--afl", 5)) continue;
+  } else if (strncmp(aflcc->callname, "afl-gcc", 7) == 0 ||
 
-    if (lto_mode && !strncmp(cur, "-flto=thin", 10)) {
+             strncmp(aflcc->callname, "afl-g++", 7) == 0) {
 
-      FATAL(
-          "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or "
-          "use afl-clang-fast!");
+    aflcc->compiler_mode = GCC;
 
-    }
+#endif
 
-    if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue;
-    if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue;
-    if (!strncmp(cur, "-fno-unroll", 11)) continue;
-    if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue;
-    if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") ||
-        !strcmp(cur, "--no-undefined")) {
+#if defined(__x86_64__)
 
-      continue;
+  } else if (strcmp(aflcc->callname, "afl-clang") == 0 ||
 
-    }
+             strcmp(aflcc->callname, "afl-clang++") == 0) {
 
-    if (compiler_mode == GCC_PLUGIN && !strcmp(cur, "-pipe")) { continue; }
+    aflcc->compiler_mode = CLANG;
 
-    if (!strcmp(cur, "-z") || !strcmp(cur, "-Wl,-z")) {
+#endif
 
-      u8 *param = *(argv + 1);
-      if (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs")) {
+  }
 
-        skip_next = 1;
-        continue;
+}
 
-      }
+void compiler_mode_by_environ(aflcc_state_t *aflcc) {
 
-    }
+  if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) {
 
-    if ((compiler_mode == GCC || compiler_mode == GCC_PLUGIN) &&
-        !strncmp(cur, "-stdlib=", 8)) {
+    aflcc->passthrough = 1;
 
-      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
-      continue;
+  }
 
-    }
+  char *ptr = getenv("AFL_CC_COMPILER");
 
-    if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) {
+  if (!ptr) { return; }
 
-      have_instr_list = 1;
+  if (aflcc->compiler_mode) {
 
-    }
+    if (!be_quiet) {
 
-    if (!strncmp(cur, "-fsanitize=", strlen("-fsanitize=")) &&
-        strchr(cur, ',')) {
+      WARNF(
+          "\"AFL_CC_COMPILER\" is set but a specific compiler was already "
+          "selected by command line parameter or symlink, ignoring the "
+          "environment variable!");
 
-      parse_fsanitize(cur);
-      if (!cur || strlen(cur) <= strlen("-fsanitize=")) { continue; }
+    }
 
-    } else if ((!strncmp(cur, "-fsanitize=fuzzer-",
+  } else {
 
-                         strlen("-fsanitize=fuzzer-")) ||
-                !strncmp(cur, "-fsanitize-coverage",
-                         strlen("-fsanitize-coverage"))) &&
-               (strncmp(cur, "sanitize-coverage-allow",
-                        strlen("sanitize-coverage-allow")) &&
-                strncmp(cur, "sanitize-coverage-deny",
-                        strlen("sanitize-coverage-deny")) &&
-                instrument_mode != INSTRUMENT_LLVMNATIVE)) {
+    if (strncasecmp(ptr, "LTO", 3) == 0) {
 
-      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
-      continue;
+      aflcc->compiler_mode = LTO;
 
-    }
+    } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
 
-    if (need_aflpplib || !strcmp(cur, "-fsanitize=fuzzer")) {
+      aflcc->compiler_mode = LLVM;
 
-      u8 *afllib = find_object("libAFLDriver.a", argv[0]);
+    } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
 
-      if (!be_quiet) {
+               strncasecmp(ptr, "GCC-P", 5) == 0 ||
+               strncasecmp(ptr, "GCCP", 4) == 0) {
 
-        OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
+      aflcc->compiler_mode = GCC_PLUGIN;
 
-      }
+#if defined(__x86_64__)
 
-      if (!afllib) {
+    } else if (strcasecmp(ptr, "GCC") == 0) {
 
-        if (!be_quiet) {
+      aflcc->compiler_mode = GCC;
 
-          WARNF(
-              "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
-              "the flags - this will fail!");
+#endif
 
-        }
+#if defined(__x86_64__)
 
-      } else {
+    } else if (strcasecmp(ptr, "CLANG") == 0) {
 
-        cc_params[cc_par_cnt++] = afllib;
+      aflcc->compiler_mode = CLANG;
 
-#ifdef __APPLE__
-        cc_params[cc_par_cnt++] = "-undefined";
-        cc_params[cc_par_cnt++] = "dynamic_lookup";
 #endif
 
-      }
+    } else
 
-      if (need_aflpplib) {
+      FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr);
 
-        need_aflpplib = 0;
+  }
 
-      } else {
+}
 
-        continue;
+// If it can be inferred, instrument_mode would also be set
+void compiler_mode_by_cmdline(aflcc_state_t *aflcc, int argc, char **argv) {
 
-      }
+  char *ptr = NULL;
 
-    }
+  for (int i = 1; i < argc; i++) {
 
-    if (!strcmp(cur, "-m32")) bit_mode = 32;
-    if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
-    if (!strcmp(cur, "-m64")) bit_mode = 64;
+    if (strncmp(argv[i], "--afl", 5) == 0) {
 
-    if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
-      asan_set = 1;
+      if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) {
 
-    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
+        aflcc->passthrough = 1;
+        argv[i] = "-g";  // we have to overwrite it, -g is always good
+        continue;
 
-    if (!strcmp(cur, "-x")) x_set = 1;
-    if (!strcmp(cur, "-E")) preprocessor_only = 1;
-    if (!strcmp(cur, "-shared")) shared_linking = 1;
-    if (!strcmp(cur, "-dynamiclib")) shared_linking = 1;
-    if (!strcmp(cur, "--target=wasm32-wasi")) passthrough = 1;
-    if (!strcmp(cur, "-Wl,-r")) partial_linking = 1;
-    if (!strcmp(cur, "-Wl,-i")) partial_linking = 1;
-    if (!strcmp(cur, "-Wl,--relocatable")) partial_linking = 1;
-    if (!strcmp(cur, "-r")) partial_linking = 1;
-    if (!strcmp(cur, "--relocatable")) partial_linking = 1;
-    if (!strcmp(cur, "-c")) have_c = 1;
+      }
 
-    if (!strncmp(cur, "-O", 2)) have_o = 1;
-    if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1;
+      if (aflcc->compiler_mode && !be_quiet) {
 
-    if (*cur == '@') {
+        WARNF(
+            "--afl-... compiler mode supersedes the AFL_CC_COMPILER and "
+            "symlink compiler selection!");
 
-      // response file support.
-      // we have two choices - move everything to the command line or
-      // rewrite the response files to temporary files and delete them
-      // afterwards. We choose the first for easiness.
-      // We do *not* support quotes in the rsp files to cope with spaces in
-      // filenames etc! If you need that then send a patch!
-      u8 *filename = cur + 1;
-      if (debug) { DEBUGF("response file=%s\n", filename); }
-      FILE       *f = fopen(filename, "r");
-      struct stat st;
+      }
 
-      // Check not found or empty? let the compiler complain if so.
-      if (!f || fstat(fileno(f), &st) < 0 || st.st_size < 1) {
+      ptr = argv[i];
+      ptr += 5;
+      while (*ptr == '-')
+        ptr++;
 
-        cc_params[cc_par_cnt++] = cur;
-        continue;
+      if (strncasecmp(ptr, "LTO", 3) == 0) {
 
-      }
+        aflcc->compiler_mode = LTO;
 
-      u8    *tmpbuf = malloc(st.st_size + 2), *ptr;
-      char **args = malloc(sizeof(char *) * (st.st_size >> 1));
-      int    count = 1, cont = 0, cont_act = 0;
+      } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
 
-      while (fgets(tmpbuf, st.st_size + 1, f)) {
+        aflcc->compiler_mode = LLVM;
 
-        ptr = tmpbuf;
-        // fprintf(stderr, "1: %s\n", ptr);
-        //  no leading whitespace
-        while (isspace(*ptr)) {
+      } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 ||
 
-          ++ptr;
-          cont_act = 0;
+                 strncasecmp(ptr, "PC-GUARD", 8) == 0) {
 
-        }
+        aflcc->compiler_mode = LLVM;
+        aflcc->instrument_mode = INSTRUMENT_PCGUARD;
 
-        // no comments, no empty lines
-        if (*ptr == '#' || *ptr == '\n' || !*ptr) { continue; }
-        // remove LF
-        if (ptr[strlen(ptr) - 1] == '\n') { ptr[strlen(ptr) - 1] = 0; }
-        // remove CR
-        if (*ptr && ptr[strlen(ptr) - 1] == '\r') { ptr[strlen(ptr) - 1] = 0; }
-        // handle \ at end of line
-        if (*ptr && ptr[strlen(ptr) - 1] == '\\') {
+      } else if (strcasecmp(ptr, "INSTRIM") == 0 ||
 
-          cont = 1;
-          ptr[strlen(ptr) - 1] = 0;
+                 strcasecmp(ptr, "CFG") == 0) {
 
-        }
+        FATAL(
+            "InsTrim instrumentation was removed. Use a modern LLVM and "
+            "PCGUARD (default in afl-cc).\n");
 
-        // fprintf(stderr, "2: %s\n", ptr);
+      } else if (strcasecmp(ptr, "AFL") == 0 ||
 
-        // remove whitespace at end
-        while (*ptr && isspace(ptr[strlen(ptr) - 1])) {
+                 strcasecmp(ptr, "CLASSIC") == 0) {
 
-          ptr[strlen(ptr) - 1] = 0;
-          cont = 0;
+        aflcc->compiler_mode = LLVM;
+        aflcc->instrument_mode = INSTRUMENT_CLASSIC;
 
-        }
+      } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 ||
 
-        // fprintf(stderr, "3: %s\n", ptr);
-        if (*ptr) {
+                 strcasecmp(ptr, "NATIVE") == 0 ||
+                 strcasecmp(ptr, "LLVM-NATIVE") == 0) {
 
-          do {
+        aflcc->compiler_mode = LLVM;
+        aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-            u8 *value = ptr;
-            while (*ptr && !isspace(*ptr)) {
+      } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
 
-              ++ptr;
+                 strncasecmp(ptr, "GCC-P", 5) == 0 ||
+                 strncasecmp(ptr, "GCCP", 4) == 0) {
 
-            }
+        aflcc->compiler_mode = GCC_PLUGIN;
 
-            while (*ptr && isspace(*ptr)) {
+#if defined(__x86_64__)
 
-              *ptr++ = 0;
+      } else if (strcasecmp(ptr, "GCC") == 0) {
 
-            }
+        aflcc->compiler_mode = GCC;
 
-            if (cont_act) {
+#endif
 
-              u32 len = strlen(args[count - 1]) + strlen(value) + 1;
-              u8 *tmp = malloc(len);
-              snprintf(tmp, len, "%s%s", args[count - 1], value);
-              free(args[count - 1]);
-              args[count - 1] = tmp;
-              cont_act = 0;
+#if defined(__x86_64__)
 
-            } else {
+      } else if (strncasecmp(ptr, "CLANG", 5) == 0) {
 
-              args[count++] = strdup(value);
+        aflcc->compiler_mode = CLANG;
 
-            }
+#endif
 
-          } while (*ptr);
+      } else
 
-        }
+        FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]);
 
-        if (cont) {
+    }
 
-          cont_act = 1;
-          cont = 0;
+  }
 
-        }
+}
 
-      }
+static void instrument_mode_old_environ(aflcc_state_t *aflcc) {
 
-      if (count) { process_params(count, args); }
+  if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") ||
+      getenv("INSTRIM_LIB")) {
 
-      // we cannot free args[]
-      free(tmpbuf);
+    FATAL(
+        "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD "
+        "(default in afl-cc).\n");
 
-      continue;
+  }
 
-    }
+  if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
+      getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
 
-    cc_params[cc_par_cnt++] = cur;
+    if (aflcc->instrument_mode == 0)
+      aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+    else if (aflcc->instrument_mode != INSTRUMENT_PCGUARD)
+      FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together");
 
   }
 
-}
+  if (getenv("AFL_LLVM_CTX")) aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX;
+  if (getenv("AFL_LLVM_CALLER"))
+    aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
 
-/* Copy argv to cc_params, making the necessary edits. */
-
-static void edit_params(u32 argc, char **argv, char **envp) {
+  if (getenv("AFL_LLVM_NGRAM_SIZE")) {
 
-  cc_params = ck_alloc(MAX_PARAMS_NUM * sizeof(u8 *));
+    aflcc->instrument_opt_mode |= INSTRUMENT_OPT_NGRAM;
+    aflcc->ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE"));
+    if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX)
+      FATAL(
+          "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX "
+          "(%u)",
+          NGRAM_SIZE_MAX);
 
-  for (u32 c = 1; c < argc; ++c) {
+  }
 
-    if (!strcmp(argv[c], "-c")) have_c = 1;
-    if (!strncmp(argv[c], "-fsanitize-coverage-", 20) &&
-        strstr(argv[c], "list=")) {
+  if (getenv("AFL_LLVM_CTX_K")) {
 
-      have_instr_list = 1;
+    aflcc->ctx_k = atoi(getenv("AFL_LLVM_CTX_K"));
+    if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K)
+      FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)",
+            CTX_MAX_K);
+    if (aflcc->ctx_k == 1) {
 
-    }
+      setenv("AFL_LLVM_CALLER", "1", 1);
+      unsetenv("AFL_LLVM_CTX_K");
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
 
-  }
+    } else {
 
-  if (lto_mode) {
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX_K;
 
-    if (lto_flag[0] != '-')
-      FATAL(
-          "Using afl-clang-lto is not possible because Makefile magic did not "
-          "identify the correct -flto flag");
-    else
-      compiler_mode = LTO;
+    }
 
   }
 
-  if (plusplus_mode) {
+}
 
-    u8 *alt_cxx = getenv("AFL_CXX");
+// compiler_mode would also be set if depended by the instrument_mode
+static void instrument_mode_new_environ(aflcc_state_t *aflcc) {
 
-    if (!alt_cxx) {
+  if (!getenv("AFL_LLVM_INSTRUMENT")) { return; }
 
-      if (compiler_mode >= GCC_PLUGIN) {
+  u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;");
 
-        if (compiler_mode == GCC) {
+  while (ptr2) {
 
-          alt_cxx = clang_mode ? "clang++" : "g++";
+    if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 ||
+        strncasecmp(ptr2, "classic", strlen("classic")) == 0) {
 
-        } else if (compiler_mode == CLANG) {
+      if (aflcc->instrument_mode == INSTRUMENT_LTO) {
 
-          alt_cxx = "clang++";
+        aflcc->instrument_mode = INSTRUMENT_CLASSIC;
+        aflcc->lto_mode = 1;
 
-        } else {
+      } else if (!aflcc->instrument_mode ||
 
-          alt_cxx = "g++";
+                 aflcc->instrument_mode == INSTRUMENT_AFL) {
 
-        }
+        aflcc->instrument_mode = INSTRUMENT_AFL;
 
       } else {
 
-        if (USE_BINDIR)
-          snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++",
-                   LLVM_BINDIR);
-        else
-          snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN);
-        alt_cxx = llvm_fullpath;
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
       }
 
     }
 
-    cc_params[0] = alt_cxx;
+    if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 ||
+        strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) {
 
-  } else {
+      if (!aflcc->instrument_mode ||
+          aflcc->instrument_mode == INSTRUMENT_PCGUARD)
 
-    u8 *alt_cc = getenv("AFL_CC");
+        aflcc->instrument_mode = INSTRUMENT_PCGUARD;
 
-    if (!alt_cc) {
+      else
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
-      if (compiler_mode >= GCC_PLUGIN) {
+    }
 
-        if (compiler_mode == GCC) {
+    if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 ||
+        strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 ||
+        strncasecmp(ptr2, "native", strlen("native")) == 0) {
 
-          alt_cc = clang_mode ? "clang" : "gcc";
+      if (!aflcc->instrument_mode ||
+          aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE)
 
-        } else if (compiler_mode == CLANG) {
+        aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-          alt_cc = "clang";
+      else
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
-        } else {
+    }
 
-          alt_cc = "gcc";
+    if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 ||
+        strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) {
 
-        }
+      if (!aflcc->instrument_mode ||
+          aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) {
+
+        aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
+        aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CODECOV;
 
       } else {
 
-        if (USE_BINDIR)
-          snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang",
-                   LLVM_BINDIR);
-        else
-          snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s", CLANG_BIN);
-        alt_cc = llvm_fullpath;
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
       }
 
     }
 
-    cc_params[0] = alt_cc;
+    if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 ||
+        strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) {
 
-  }
+      FATAL(
+          "InsTrim instrumentation was removed. Use a modern LLVM and "
+          "PCGUARD (default in afl-cc).\n");
 
-  if (compiler_mode == GCC || compiler_mode == CLANG) {
+    }
+
+    if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) {
 
-    cc_params[cc_par_cnt++] = "-B";
-    cc_params[cc_par_cnt++] = obj_path;
+      aflcc->lto_mode = 1;
+      if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_LTO)
 
-    if (clang_mode || compiler_mode == CLANG) {
+        aflcc->instrument_mode = INSTRUMENT_LTO;
 
-      cc_params[cc_par_cnt++] = "-no-integrated-as";
+      else
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
     }
 
-  }
+#if defined(__x86_64__)
+    if (strcasecmp(ptr2, "gcc") == 0) {
 
-  if (compiler_mode == GCC_PLUGIN) {
+      if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_GCC)
 
-    char *fplugin_arg;
+        aflcc->instrument_mode = INSTRUMENT_GCC;
 
-    if (cmplog_mode) {
+      else if (aflcc->instrument_mode != INSTRUMENT_GCC)
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
-      fplugin_arg =
-          alloc_printf("-fplugin=%s/afl-gcc-cmplog-pass.so", obj_path);
-      cc_params[cc_par_cnt++] = fplugin_arg;
-      fplugin_arg =
-          alloc_printf("-fplugin=%s/afl-gcc-cmptrs-pass.so", obj_path);
-      cc_params[cc_par_cnt++] = fplugin_arg;
+      aflcc->compiler_mode = GCC;
 
     }
 
-    fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path);
-    cc_params[cc_par_cnt++] = fplugin_arg;
-    cc_params[cc_par_cnt++] = "-fno-if-conversion";
-    cc_params[cc_par_cnt++] = "-fno-if-conversion2";
+#endif
 
-  }
+#if defined(__x86_64__)
+    if (strcasecmp(ptr2, "clang") == 0) {
 
-  if (compiler_mode == LLVM || compiler_mode == LTO) {
+      if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_CLANG)
 
-    cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument";
+        aflcc->instrument_mode = INSTRUMENT_CLANG;
 
-    if (lto_mode && have_instr_env) {
+      else if (aflcc->instrument_mode != INSTRUMENT_CLANG)
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] = alloc_printf(
-          "-fpass-plugin=%s/afl-llvm-lto-instrumentlist.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path);
-#endif
+      aflcc->compiler_mode = CLANG;
 
     }
 
-    if (getenv("AFL_LLVM_DICT2FILE")) {
-
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/afl-llvm-dict2file.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/afl-llvm-dict2file.so", obj_path);
 #endif
 
-    }
+    if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 ||
+        strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 ||
+        strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) {
 
-    // laf
-    if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
+      u8 *ptr3 = ptr2;
+      while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9'))
+        ptr3++;
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/split-switches-pass.so", obj_path);
-#endif
+      if (!*ptr3) {
+
+        if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL)
+          FATAL(
+              "you must set the K-CTX K with (e.g. for value 2) "
+              "AFL_LLVM_INSTRUMENT=ctx-2");
+
+      }
+
+      aflcc->ctx_k = atoi(ptr3);
+      if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K)
+        FATAL(
+            "K-CTX instrumentation option must be between 1 and CTX_MAX_K "
+            "(%u)",
+            CTX_MAX_K);
+
+      if (aflcc->ctx_k == 1) {
+
+        aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+        setenv("AFL_LLVM_CALLER", "1", 1);
+        unsetenv("AFL_LLVM_CTX_K");
+
+      } else {
+
+        aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K);
+        u8 *ptr4 = alloc_printf("%u", aflcc->ctx_k);
+        setenv("AFL_LLVM_CTX_K", ptr4, 1);
+
+      }
 
     }
 
-    if (getenv("LAF_TRANSFORM_COMPARES") ||
-        getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
+    if (strcasecmp(ptr2, "ctx") == 0) {
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/compare-transform-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/compare-transform-pass.so", obj_path);
-#endif
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX;
+      setenv("AFL_LLVM_CTX", "1", 1);
 
     }
 
-    if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") ||
-        getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) {
+    if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) {
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/split-compares-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/split-compares-pass.so", obj_path);
-#endif
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+      setenv("AFL_LLVM_CALLER", "1", 1);
 
     }
 
-    // /laf
+    if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) {
 
-    unsetenv("AFL_LD");
-    unsetenv("AFL_LD_CALLER");
+      u8 *ptr3 = ptr2 + strlen("ngram");
+      while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) {
 
-    if (cmplog_mode) {
+        ptr3++;
 
-      cc_params[cc_par_cnt++] = "-fno-inline";
+      }
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/cmplog-switches-pass.so", obj_path);
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/cmplog-switches-pass.so", obj_path);
+      if (!*ptr3) {
 
-      // reuse split switches from laf
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/split-switches-pass.so", obj_path);
-#endif
+        if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL)
+          FATAL(
+              "you must set the NGRAM size with (e.g. for value 2) "
+              "AFL_LLVM_INSTRUMENT=ngram-2");
+
+      }
+
+      aflcc->ngram_size = atoi(ptr3);
+
+      if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) {
+
+        FATAL(
+            "NGRAM instrumentation option must be between 2 and "
+            "NGRAM_SIZE_MAX (%u)",
+            NGRAM_SIZE_MAX);
+
+      }
+
+      aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM);
+      u8 *ptr4 = alloc_printf("%u", aflcc->ngram_size);
+      setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1);
 
     }
 
-    // #if LLVM_MAJOR >= 13
-    //     // Use the old pass manager in LLVM 14 which the AFL++ passes still
-    //     use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager";
-    // #endif
+    ptr2 = strtok(NULL, ":,;");
 
-    if (lto_mode && !have_c) {
+  }
 
-      u8 *ld_path = NULL;
-      if (getenv("AFL_REAL_LD")) {
+}
 
-        ld_path = strdup(getenv("AFL_REAL_LD"));
+void instrument_mode_by_environ(aflcc_state_t *aflcc) {
 
-      } else {
+  if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") ||
+      getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") ||
+      getenv("AFL_LLVM_BLOCKLIST")) {
 
-        ld_path = strdup(AFL_REAL_LD);
+    aflcc->have_instr_env = 1;
 
-      }
+  }
 
-      if (!ld_path || !*ld_path) {
+  if (aflcc->have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) {
 
-        if (ld_path) {
+    WARNF(
+        "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined "
+        "for file matching, only function matching!");
 
-          // Freeing empty string
-          free(ld_path);
+  }
 
-        }
+  instrument_mode_old_environ(aflcc);
+  instrument_mode_new_environ(aflcc);
 
-        ld_path = strdup("ld.lld");
+}
 
-      }
+static void instrument_opt_mode_exclude(aflcc_state_t *aflcc) {
 
-      if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); }
-#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12
-      cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", ld_path);
-#else
-      cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_path);
-#endif
-      free(ld_path);
+  if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER)) {
 
-#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15
-      // The NewPM implementation only works fully since LLVM 15.
-      cc_params[cc_par_cnt++] = alloc_printf(
-          "-Wl,--load-pass-plugin=%s/SanitizerCoverageLTO.so", obj_path);
-#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13
-      cc_params[cc_par_cnt++] = "-Wl,--lto-legacy-pass-manager";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path);
-#endif
+    FATAL("you cannot set CTX and CALLER together");
 
-      cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition";
-      cc_params[cc_par_cnt++] = lto_flag;
+  }
 
-    } else {
+  if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
 
-      if (instrument_mode == INSTRUMENT_PCGUARD) {
+    FATAL("you cannot set CTX and K-CTX together");
 
-#if LLVM_MAJOR >= 13
-  #if defined __ANDROID__ || ANDROID
-        cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
-        instrument_mode = INSTRUMENT_LLVMNATIVE;
-  #else
-        if (have_instr_list) {
-
-          if (!be_quiet)
-            SAYF(
-                "Using unoptimized trace-pc-guard, due usage of "
-                "-fsanitize-coverage-allow/denylist, you can use "
-                "AFL_LLVM_ALLOWLIST/AFL_LLMV_DENYLIST instead.\n");
-          cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
-          instrument_mode = INSTRUMENT_LLVMNATIVE;
-
-        } else {
-
-    #if LLVM_MAJOR >= 13                            /* use new pass manager */
-      #if LLVM_MAJOR < 16
-          cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-      #endif
-          cc_params[cc_par_cnt++] = alloc_printf(
-              "-fpass-plugin=%s/SanitizerCoveragePCGUARD.so", obj_path);
-    #else
-          cc_params[cc_par_cnt++] = "-Xclang";
-          cc_params[cc_par_cnt++] = "-load";
-          cc_params[cc_par_cnt++] = "-Xclang";
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path);
-    #endif
+  }
 
-        }
+  if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) &&
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
 
-  #endif
-#else
-  #if LLVM_MAJOR >= 4
-        if (!be_quiet)
-          SAYF(
-              "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for "
-              "enhanced version.\n");
-        cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
-        instrument_mode = INSTRUMENT_LLVMNATIVE;
-  #else
-        FATAL("pcguard instrumentation requires LLVM 4.0.1+");
-  #endif
-#endif
+    FATAL("you cannot set CALLER and K-CTX together");
 
-      } else if (instrument_mode == INSTRUMENT_LLVMNATIVE) {
+  }
 
-#if LLVM_MAJOR >= 4
-        if (instrument_opt_mode & INSTRUMENT_OPT_CODECOV) {
+  if (aflcc->instrument_opt_mode && aflcc->compiler_mode != LLVM)
+    FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode");
 
-  #if LLVM_MAJOR >= 6
-          cc_params[cc_par_cnt++] =
-              "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table";
-  #else
-          FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+");
-  #endif
+  if (aflcc->instrument_opt_mode &&
+      aflcc->instrument_opt_mode != INSTRUMENT_OPT_CODECOV &&
+      aflcc->instrument_mode != INSTRUMENT_CLASSIC)
+    FATAL(
+        "CALLER, CTX and NGRAM instrumentation options can only be used with "
+        "the LLVM CLASSIC instrumentation mode.");
 
-        } else {
+}
 
-          cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
+void mode_final_checkout(aflcc_state_t *aflcc, int argc, char **argv) {
 
-        }
+  if (aflcc->instrument_opt_mode &&
+      aflcc->instrument_mode == INSTRUMENT_DEFAULT &&
+      (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == UNSET)) {
 
-#else
-        FATAL("pcguard instrumentation requires LLVM 4.0.1+");
-#endif
+    aflcc->instrument_mode = INSTRUMENT_CLASSIC;
+    aflcc->compiler_mode = LLVM;
 
-      } else {
+  }
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-        cc_params[cc_par_cnt++] =
-            alloc_printf("-fpass-plugin=%s/afl-llvm-pass.so", obj_path);
-#else
+  if (!aflcc->compiler_mode) {
 
-        cc_params[cc_par_cnt++] = "-Xclang";
-        cc_params[cc_par_cnt++] = "-load";
-        cc_params[cc_par_cnt++] = "-Xclang";
-        cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
-#endif
+    // lto is not a default because outside of afl-cc RANLIB and AR have to
+    // be set to LLVM versions so this would work
+    if (aflcc->have_llvm)
+      aflcc->compiler_mode = LLVM;
+    else if (aflcc->have_gcc_plugin)
+      aflcc->compiler_mode = GCC_PLUGIN;
+    else if (aflcc->have_gcc)
+      aflcc->compiler_mode = GCC;
+    else if (aflcc->have_clang)
+      aflcc->compiler_mode = CLANG;
+    else if (aflcc->have_lto)
+      aflcc->compiler_mode = LTO;
+    else
+      FATAL("no compiler mode available");
 
-      }
+  }
+
+  switch (aflcc->compiler_mode) {
+
+    case GCC:
+      if (!aflcc->have_gcc) FATAL("afl-gcc not available on your platform!");
+      break;
+    case CLANG:
+      if (!aflcc->have_clang)
+        FATAL("afl-clang not available on your platform!");
+      break;
+    case LLVM:
+      if (!aflcc->have_llvm)
+        FATAL(
+            "LLVM mode is not available, please install LLVM 13+ and recompile "
+            "AFL++");
+      break;
+    case GCC_PLUGIN:
+      if (!aflcc->have_gcc_plugin)
+        FATAL(
+            "GCC_PLUGIN mode is not available, install gcc plugin support and "
+            "recompile AFL++");
+      break;
+    case LTO:
+      if (!aflcc->have_lto)
+        FATAL(
+            "LTO mode is not available, please install LLVM 13+ and lld of the "
+            "same version and recompile AFL++");
+      break;
+    default:
+      FATAL("no compiler mode available");
+
+  }
+
+  if (aflcc->compiler_mode == GCC) { aflcc->instrument_mode = INSTRUMENT_GCC; }
+
+  if (aflcc->compiler_mode == CLANG) {
+
+    /* if our PCGUARD implementation is not available then silently switch to
+     native LLVM PCGUARD. Or classic asm instrument is explicitly preferred. */
+    if (!aflcc->have_optimized_pcguard &&
+        (aflcc->instrument_mode == INSTRUMENT_DEFAULT ||
+         aflcc->instrument_mode == INSTRUMENT_PCGUARD)) {
+
+      aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
+
+    } else {
+
+      aflcc->instrument_mode = INSTRUMENT_CLANG;
+      setenv(CLANG_ENV_VAR, "1", 1);  // used by afl-as
 
     }
 
-    if (cmplog_mode) {
+  }
 
-#if LLVM_MAJOR >= 11
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] = alloc_printf(
-          "-fpass-plugin=%s/cmplog-instructions-pass.so", obj_path);
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/cmplog-routines-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/cmplog-instructions-pass.so", obj_path);
-
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/cmplog-routines-pass.so", obj_path);
-#endif
+  if (aflcc->compiler_mode == LTO) {
+
+    if (aflcc->instrument_mode == 0 ||
+        aflcc->instrument_mode == INSTRUMENT_LTO ||
+        aflcc->instrument_mode == INSTRUMENT_CFG ||
+        aflcc->instrument_mode == INSTRUMENT_PCGUARD) {
+
+      aflcc->lto_mode = 1;
+      // force CFG
+      // if (!aflcc->instrument_mode) {
+
+      aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+
+      // }
+
+    } else if (aflcc->instrument_mode == INSTRUMENT_CLASSIC) {
+
+      aflcc->lto_mode = 1;
+
+    } else {
+
+      if (!be_quiet) {
+
+        WARNF("afl-clang-lto called with mode %s, using that mode instead",
+              instrument_mode_2str(aflcc->instrument_mode));
+
+      }
 
     }
 
-    if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
-        getenv("AFL_LLVM_INJECTIONS_SQL") ||
-        getenv("AFL_LLVM_INJECTIONS_LDAP") ||
-        getenv("AFL_LLVM_INJECTIONS_XSS")) {
+  }
+
+  if (aflcc->instrument_mode == 0 && aflcc->compiler_mode < GCC_PLUGIN) {
+
+#if LLVM_MAJOR >= 7
+  #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
+    if (aflcc->have_instr_env) {
+
+      aflcc->instrument_mode = INSTRUMENT_AFL;
+      if (!be_quiet) {
+
+        WARNF(
+            "Switching to classic instrumentation because "
+            "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1.");
+
+      }
+
+    } else
 
-#if LLVM_MAJOR >= 11
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
   #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/injection-pass.so", obj_path);
+      aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+
 #else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = alloc_printf("%s/injection-pass.so", obj_path);
+    aflcc->instrument_mode = INSTRUMENT_AFL;
 #endif
 
-    }
+  }
 
-    // cc_params[cc_par_cnt++] = "-Qunused-arguments";
+  if (!aflcc->instrument_opt_mode && aflcc->lto_mode &&
+      aflcc->instrument_mode == INSTRUMENT_CFG) {
 
-  }
+    aflcc->instrument_mode = INSTRUMENT_PCGUARD;
 
-  /* Inspect the command line parameters. */
+  }
 
-  process_params(argc, argv);
+#ifndef AFL_CLANG_FLTO
+  if (aflcc->lto_mode)
+    FATAL(
+        "instrumentation mode LTO specified but LLVM support not available "
+        "(requires LLVM 11 or higher)");
+#endif
 
-  if (!have_pic) {
+  if (aflcc->lto_mode) {
 
-    cc_params[cc_par_cnt++] = "-fPIC";
-    have_pic = 1;
+    if (aflcc->lto_flag[0] != '-')
+      FATAL(
+          "Using afl-clang-lto is not possible because Makefile magic did not "
+          "identify the correct -flto flag");
+    else
+      aflcc->compiler_mode = LTO;
 
   }
 
-  if (compiler_mode != GCC_PLUGIN && compiler_mode != GCC &&
-      !getenv("AFL_LLVM_NO_RPATH")) {
+  if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO"))
+    FATAL(
+        "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set "
+        "together");
 
-    // in case LLVM is installed not via a package manager or "make install"
-    // e.g. compiled download or compiled from github then its ./lib directory
-    // might not be in the search path. Add it if so.
-    const char *libdir = LLVM_LIBDIR;
-    if (plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) &&
-        strncmp(libdir, "/lib", 4)) {
+#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
 
-      u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR);
-      cc_params[cc_par_cnt++] = libdir_opt;
+  if (aflcc->instrument_mode == INSTRUMENT_PCGUARD && aflcc->have_instr_env) {
 
-    }
+    FATAL(
+        "Instrumentation type PCGUARD does not support "
+        "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead.");
 
   }
 
-  if (getenv("AFL_HARDEN")) {
+#endif
 
-    cc_params[cc_par_cnt++] = "-fstack-protector-all";
+  instrument_opt_mode_exclude(aflcc);
 
-    if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
+  u8 *ptr2;
 
-  }
+  if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/')
+    FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path");
 
-  if (!asan_set) {
+  if (getenv("AFL_LLVM_LAF_ALL")) {
 
-    if (getenv("AFL_USE_ASAN")) {
+    setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1);
+    setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1);
+    setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1);
+    setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1);
 
-      if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
+  }
 
-      if (getenv("AFL_HARDEN"))
-        FATAL("ASAN and AFL_HARDEN are mutually exclusive");
+  aflcc->cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") ||
+                       getenv("AFL_GCC_CMPLOG");
 
-      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
-      cc_params[cc_par_cnt++] = "-fsanitize=address";
+}
 
-    } else if (getenv("AFL_USE_MSAN")) {
+void mode_notification(aflcc_state_t *aflcc) {
 
-      if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
+  char *ptr2 = alloc_printf(" + NGRAM-%u", aflcc->ngram_size);
+  char *ptr3 = alloc_printf(" + K-CTX-%u", aflcc->ctx_k);
 
-      if (getenv("AFL_HARDEN"))
-        FATAL("MSAN and AFL_HARDEN are mutually exclusive");
+  char *ptr1 = alloc_printf(
+      "%s%s%s%s%s", instrument_mode_2str(aflcc->instrument_mode),
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "",
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "",
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "",
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "");
 
-      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
-      cc_params[cc_par_cnt++] = "-fsanitize=memory";
+  ck_free(ptr2);
+  ck_free(ptr3);
 
-    }
+  if ((isatty(2) && !be_quiet) || aflcc->debug) {
+
+    SAYF(cCYA
+         "afl-cc" VERSION cRST
+         " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n",
+         compiler_mode_2str(aflcc->compiler_mode), ptr1);
 
   }
 
-  if (getenv("AFL_USE_UBSAN")) {
+  ck_free(ptr1);
 
-    cc_params[cc_par_cnt++] = "-fsanitize=undefined";
-    cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error";
-    cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all";
-    cc_params[cc_par_cnt++] = "-fno-omit-frame-pointer";
+  if (!be_quiet &&
+      (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG)) {
+
+    WARNF(
+        "You are using outdated instrumentation, install LLVM and/or "
+        "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast "
+        "instead!");
 
   }
 
-  if (getenv("AFL_USE_TSAN")) {
+}
 
-    cc_params[cc_par_cnt++] = "-fsanitize=thread";
-    cc_params[cc_par_cnt++] = "-fno-omit-frame-pointer";
+void add_real_argv0(aflcc_state_t *aflcc) {
 
-  }
+  static u8 llvm_fullpath[PATH_MAX];
 
-  if (getenv("AFL_USE_LSAN")) {
+  if (aflcc->plusplus_mode) {
 
-    cc_params[cc_par_cnt++] = "-fsanitize=leak";
-    cc_params[cc_par_cnt++] = "-includesanitizer/lsan_interface.h";
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) "
-        "_exit(23); }";
-    cc_params[cc_par_cnt++] = "-D__AFL_LSAN_OFF()=__lsan_disable();";
-    cc_params[cc_par_cnt++] = "-D__AFL_LSAN_ON()=__lsan_enable();";
+    u8 *alt_cxx = getenv("AFL_CXX");
 
-  }
+    if (!alt_cxx) {
 
-  if (getenv("AFL_USE_CFISAN")) {
+      if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) {
 
-    if (compiler_mode == GCC_PLUGIN || compiler_mode == GCC) {
+        alt_cxx = "g++";
 
-      cc_params[cc_par_cnt++] = "-fcf-protection=full";
+      } else if (aflcc->compiler_mode == CLANG) {
 
-    } else {
+        alt_cxx = "clang++";
 
-      if (!lto_mode) {
+      } else {
 
-        uint32_t i = 0, found = 0;
-        while (envp[i] != NULL && !found)
-          if (strncmp("-flto", envp[i++], 5) == 0) found = 1;
-        if (!found) cc_params[cc_par_cnt++] = "-flto";
+        if (USE_BINDIR)
+          snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++",
+                   LLVM_BINDIR);
+        else
+          snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN);
+        alt_cxx = llvm_fullpath;
 
       }
 
-      cc_params[cc_par_cnt++] = "-fsanitize=cfi";
-      cc_params[cc_par_cnt++] = "-fvisibility=hidden";
-
     }
 
-  }
+    aflcc->cc_params[0] = alt_cxx;
 
-  if (!getenv("AFL_DONT_OPTIMIZE")) {
+  } else {
 
-    cc_params[cc_par_cnt++] = "-g";
-    if (!have_o) cc_params[cc_par_cnt++] = "-O3";
-    if (!have_unroll) cc_params[cc_par_cnt++] = "-funroll-loops";
-    // if (strlen(march_opt) > 1 && march_opt[0] == '-')
-    //  cc_params[cc_par_cnt++] = march_opt;
+    u8 *alt_cc = getenv("AFL_CC");
 
-  }
+    if (!alt_cc) {
 
-  if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
-      getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") ||
-      lto_mode) {
+      if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) {
 
-    cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-bcmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
+        alt_cc = "gcc";
 
-  }
+      } else if (aflcc->compiler_mode == CLANG) {
 
-#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__
-  if (!have_c) cc_params[cc_par_cnt++] = "-lrt";
-#endif
+        alt_cc = "clang";
 
-  cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
-  cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
+      } else {
 
-  /* As documented in instrumentation/README.persistent_mode.md, deferred
-     forkserver initialization and persistent mode are not available in afl-gcc
-     and afl-clang. */
-  if (compiler_mode != GCC && compiler_mode != CLANG) {
+        if (USE_BINDIR)
+          snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang",
+                   LLVM_BINDIR);
+        else
+          snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANG_BIN);
+        alt_cc = llvm_fullpath;
 
-    cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
+      }
 
-    /* When the user tries to use persistent or deferred forkserver modes by
-       appending a single line to the program, we want to reliably inject a
-       signature into the binary (to be picked up by afl-fuzz) and we want
-       to call a function from the runtime .o file. This is unnecessarily
-       painful for three reasons:
+    }
 
-       1) We need to convince the compiler not to optimize out the signature.
-          This is done with __attribute__((used)).
+    aflcc->cc_params[0] = alt_cc;
 
-       2) We need to convince the linker, when called with -Wl,--gc-sections,
-          not to do the same. This is done by forcing an assignment to a
-          'volatile' pointer.
+  }
 
-       3) We need to declare __afl_persistent_loop() in the global namespace,
-          but doing this within a method in a class is hard - :: and extern "C"
-          are forbidden and __attribute__((alias(...))) doesn't work. Hence the
-          __asm__ aliasing trick.
+}
 
-     */
+/* Macro defs for the preprocessor */
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_FUZZ_INIT()="
-        "int __afl_sharedmem_fuzzing = 1;"
-        "extern unsigned int *__afl_fuzz_len;"
-        "extern unsigned char *__afl_fuzz_ptr;"
-        "unsigned char __afl_fuzz_alt[1048576];"
-        "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
+void add_defs_common(aflcc_state_t *aflcc) {
 
-  }
+  insert_param(aflcc, "-D__AFL_COMPILER=1");
+  insert_param(aflcc, "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1");
 
-  if (plusplus_mode) {
+}
+
+/* See instrumentation/README.instrument_list.md#
+    2-selective-instrumentation-with-_afl_coverage-directives */
+void add_defs_selective_instr(aflcc_state_t *aflcc) {
+
+  if (aflcc->plusplus_mode) {
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
-        "extern \"C\" void __afl_coverage_discard();"
-        "extern \"C\" void __afl_coverage_skip();"
-        "extern \"C\" void __afl_coverage_on();"
-        "extern \"C\" void __afl_coverage_off();";
+    insert_param(aflcc,
+                 "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
+                 "extern \"C\" void __afl_coverage_discard();"
+                 "extern \"C\" void __afl_coverage_skip();"
+                 "extern \"C\" void __afl_coverage_on();"
+                 "extern \"C\" void __afl_coverage_off();");
 
   } else {
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
-        "void __afl_coverage_discard();"
-        "void __afl_coverage_skip();"
-        "void __afl_coverage_on();"
-        "void __afl_coverage_off();";
+    insert_param(aflcc,
+                 "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
+                 "void __afl_coverage_discard();"
+                 "void __afl_coverage_skip();"
+                 "void __afl_coverage_on();"
+                 "void __afl_coverage_off();");
 
   }
 
-  cc_params[cc_par_cnt++] =
+  insert_param(
+      aflcc,
       "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = "
-      "1;";
-  cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ON()=__afl_coverage_on()";
-  cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()";
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()";
-  cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()";
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : "
-      "__afl_fuzz_alt_ptr)";
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : "
-      "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff "
-      "? 0 : *__afl_fuzz_len)";
+      "1;");
+  insert_param(aflcc, "-D__AFL_COVERAGE_ON()=__afl_coverage_on()");
+  insert_param(aflcc, "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()");
+  insert_param(aflcc, "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()");
+  insert_param(aflcc, "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()");
 
-  if (compiler_mode != GCC && compiler_mode != CLANG) {
+}
+
+/* As documented in instrumentation/README.persistent_mode.md, deferred
+    forkserver initialization and persistent mode are not available in afl-gcc
+    and afl-clang. */
+void add_defs_persistent_mode(aflcc_state_t *aflcc) {
+
+  if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) return;
+
+  insert_param(aflcc, "-D__AFL_HAVE_MANUAL_CONTROL=1");
+
+  /* When the user tries to use persistent or deferred forkserver modes by
+      appending a single line to the program, we want to reliably inject a
+      signature into the binary (to be picked up by afl-fuzz) and we want
+      to call a function from the runtime .o file. This is unnecessarily
+      painful for three reasons:
+
+      1) We need to convince the compiler not to optimize out the signature.
+        This is done with __attribute__((used)).
+
+      2) We need to convince the linker, when called with -Wl,--gc-sections,
+        not to do the same. This is done by forcing an assignment to a
+        'volatile' pointer.
+
+      3) We need to declare __afl_persistent_loop() in the global namespace,
+        but doing this within a method in a class is hard - :: and extern "C"
+        are forbidden and __attribute__((alias(...))) doesn't work. Hence the
+        __asm__ aliasing trick.
+
+    */
+
+  insert_param(aflcc,
+               "-D__AFL_FUZZ_INIT()="
+               "int __afl_sharedmem_fuzzing = 1;"
+               "extern unsigned int *__afl_fuzz_len;"
+               "extern unsigned char *__afl_fuzz_ptr;"
+               "unsigned char __afl_fuzz_alt[1048576];"
+               "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;");
+
+  insert_param(aflcc,
+               "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : "
+               "__afl_fuzz_alt_ptr)");
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_LOOP(_A)="
-        "({ static volatile const char *_B __attribute__((used,unused)); "
-        " _B = (const char*)\"" PERSIST_SIG
-        "\"; "
-        "extern int __afl_connected;"
+  insert_param(
+      aflcc,
+      "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : "
+      "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff "
+      "? 0 : *__afl_fuzz_len)");
+
+  insert_param(
+      aflcc,
+      "-D__AFL_LOOP(_A)="
+      "({ static volatile const char *_B __attribute__((used,unused)); "
+      " _B = (const char*)\"" PERSIST_SIG
+      "\"; "
+      "extern int __afl_connected;"
 #ifdef __APPLE__
-        "__attribute__((visibility(\"default\"))) "
-        "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
+      "__attribute__((visibility(\"default\"))) "
+      "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
 #else
-        "__attribute__((visibility(\"default\"))) "
-        "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
+      "__attribute__((visibility(\"default\"))) "
+      "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
 #endif                                                        /* ^__APPLE__ */
-        // if afl is connected, we run _A times, else once.
-        "_L(__afl_connected ? _A : 1); })";
-
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_INIT()="
-        "do { static volatile const char *_A __attribute__((used,unused)); "
-        " _A = (const char*)\"" DEFER_SIG
-        "\"; "
+      // if afl is connected, we run _A times, else once.
+      "_L(__afl_connected ? _A : 1); })");
+
+  insert_param(
+      aflcc,
+      "-D__AFL_INIT()="
+      "do { static volatile const char *_A __attribute__((used,unused)); "
+      " _A = (const char*)\"" DEFER_SIG
+      "\"; "
 #ifdef __APPLE__
-        "__attribute__((visibility(\"default\"))) "
-        "void _I(void) __asm__(\"___afl_manual_init\"); "
+      "__attribute__((visibility(\"default\"))) "
+      "void _I(void) __asm__(\"___afl_manual_init\"); "
 #else
-        "__attribute__((visibility(\"default\"))) "
-        "void _I(void) __asm__(\"__afl_manual_init\"); "
+      "__attribute__((visibility(\"default\"))) "
+      "void _I(void) __asm__(\"__afl_manual_init\"); "
 #endif                                                        /* ^__APPLE__ */
-        "_I(); } while (0)";
+      "_I(); } while (0)");
 
-  }
+}
 
-  if (x_set) {
+/* Control  _FORTIFY_SOURCE */
+void add_defs_fortify(aflcc_state_t *aflcc, u8 action) {
 
-    cc_params[cc_par_cnt++] = "-x";
-    cc_params[cc_par_cnt++] = "none";
+  switch (action) {
 
-  }
+    case 1:
+      insert_param(aflcc, "-D_FORTIFY_SOURCE=1");
+      break;
 
-  // prevent unnecessary build errors
-  if (compiler_mode != GCC_PLUGIN && compiler_mode != GCC) {
+    case 2:
+      insert_param(aflcc, "-D_FORTIFY_SOURCE=2");
+      break;
 
-    cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument";
+    default:  // OFF
+      insert_param(aflcc, "-U_FORTIFY_SOURCE");
+      break;
 
   }
 
-  if (preprocessor_only || have_c || !non_dash) {
+}
 
-    /* In the preprocessor_only case (-E), we are not actually compiling at
-       all but requesting the compiler to output preprocessed sources only.
-       We must not add the runtime in this case because the compiler will
-       simply output its binary content back on stdout, breaking any build
-       systems that rely on a separate source preprocessing step. */
-    cc_params[cc_par_cnt] = NULL;
-    return;
+void add_defs_lsan_ctrl(aflcc_state_t *aflcc) {
 
-  }
+  insert_param(aflcc, "-includesanitizer/lsan_interface.h");
+  insert_param(
+      aflcc,
+      "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) "
+      "_exit(23); }");
+  insert_param(aflcc, "-D__AFL_LSAN_OFF()=__lsan_disable();");
+  insert_param(aflcc, "-D__AFL_LSAN_ON()=__lsan_enable();");
 
-#ifndef __ANDROID__
+}
 
-  if (compiler_mode != GCC && compiler_mode != CLANG) {
+/* About fsanitize (including PCGUARD features) */
 
-    switch (bit_mode) {
+/* For input "-fsanitize=...", it:
 
-      case 0:
-        if (!shared_linking && !partial_linking)
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-compiler-rt.o", obj_path);
-        if (lto_mode)
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-llvm-rt-lto.o", obj_path);
-        break;
+  1. may have various OOB traps :) if ... doesn't contain ',' or
+    the input has bad syntax such as "-fsantiz=,"
+  2. strips any fuzzer* in ... and writes back (may result in "-fsanitize=")
+  3. rets 1 if exactly "fuzzer" found, otherwise rets 0
+*/
+static u8 fsanitize_fuzzer_comma(char *string) {
 
-      case 32:
-        if (!shared_linking && !partial_linking) {
+  u8 detect_single_fuzzer = 0;
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-compiler-rt-32.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m32 is not supported by your compiler");
+  char *p, *ptr = string + strlen("-fsanitize=");
+  // ck_alloc will check alloc failure
+  char *new = ck_alloc(strlen(string) + 1);
+  char *tmp = ck_alloc(strlen(ptr) + 1);
+  u32   count = 0, len, ende = 0;
 
-        }
+  strcpy(new, "-fsanitize=");
 
-        if (lto_mode) {
+  do {
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m32 is not supported by your compiler");
+    p = strchr(ptr, ',');
+    if (!p) {
 
-        }
+      p = ptr + strlen(ptr) + 1;
+      ende = 1;
 
-        break;
+    }
 
-      case 64:
-        if (!shared_linking && !partial_linking) {
+    len = p - ptr;
+    if (len) {
+
+      strncpy(tmp, ptr, len);
+      tmp[len] = 0;
+      // fprintf(stderr, "Found: %s\n", tmp);
+      ptr += len + 1;
+      if (*tmp) {
+
+        u32 copy = 1;
+        if (!strcmp(tmp, "fuzzer")) {
+
+          detect_single_fuzzer = 1;
+          copy = 0;
+
+        } else if (!strncmp(tmp, "fuzzer", 6)) {
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-compiler-rt-64.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m64 is not supported by your compiler");
+          copy = 0;
 
         }
 
-        if (lto_mode) {
+        if (copy) {
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m64 is not supported by your compiler");
+          if (count) { strcat(new, ","); }
+          strcat(new, tmp);
+          ++count;
 
         }
 
-        break;
+      }
+
+    } else {
+
+      ptr++;
 
     }
 
-  #if !defined(__APPLE__) && !defined(__sun)
-    if (!shared_linking && !partial_linking)
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path);
-  #endif
+  } while (!ende);
 
-  #if defined(__APPLE__)
-    if (shared_linking || partial_linking) {
+  strcpy(string, new);
+
+  ck_free(tmp);
+  ck_free(new);
+
+  return detect_single_fuzzer;
+
+}
 
-      cc_params[cc_par_cnt++] = "-Wl,-U";
-      cc_params[cc_par_cnt++] = "-Wl,___afl_area_ptr";
-      cc_params[cc_par_cnt++] = "-Wl,-U";
-      cc_params[cc_par_cnt++] = "-Wl,___sanitizer_cov_trace_pc_guard_init";
+param_st parse_fsanitize(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) {
+
+  param_st final_ = PARAM_MISS;
+
+  if (!strncmp(cur_argv, "-fsanitize-coverage-", 20) &&
+      strstr(cur_argv, "list=")) {
+
+    if (scan) {
+
+      aflcc->have_instr_list = 1;
+      final_ = PARAM_SCAN;
+
+    } else {
+
+      final_ = PARAM_KEEP;  // may be set to DROP next
 
     }
 
-  #endif
+  }
+
+  if (!strcmp(cur_argv, "-fsanitize=fuzzer")) {
+
+    if (scan) {
+
+      aflcc->need_aflpplib = 1;
+      final_ = PARAM_SCAN;
+
+    } else {
+
+      final_ = PARAM_DROP;
+
+    }
+
+  } else if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize=")) &&
+
+             strchr(cur_argv, ',') &&
+             !strstr(cur_argv, "=,")) {  // avoid OOB errors
+
+    if (scan) {
+
+      u8 *cur_argv_ = ck_strdup(cur_argv);
+
+      if (fsanitize_fuzzer_comma(cur_argv_)) {
+
+        aflcc->need_aflpplib = 1;
+        final_ = PARAM_SCAN;
+
+      }
+
+      ck_free(cur_argv_);
+
+    } else {
+
+      fsanitize_fuzzer_comma(cur_argv);
+      if (!cur_argv || strlen(cur_argv) <= strlen("-fsanitize="))
+        final_ = PARAM_DROP;  // this means it only has "fuzzer" previously.
+
+    }
+
+  } else if ((!strncmp(cur_argv, "-fsanitize=fuzzer-",
+
+                       strlen("-fsanitize=fuzzer-")) ||
+              !strncmp(cur_argv, "-fsanitize-coverage",
+                       strlen("-fsanitize-coverage"))) &&
+             (strncmp(cur_argv, "sanitize-coverage-allow",
+                      strlen("sanitize-coverage-allow")) &&
+              strncmp(cur_argv, "sanitize-coverage-deny",
+                      strlen("sanitize-coverage-deny")) &&
+              aflcc->instrument_mode != INSTRUMENT_LLVMNATIVE)) {
+
+    if (scan) {
+
+      final_ = PARAM_SCAN;
+
+    } else {
+
+      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur_argv); }
+      final_ = PARAM_DROP;
+
+    }
 
   }
 
-  #if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__
-  cc_params[cc_par_cnt++] = "-lrt";
-  #endif
+  if (!strcmp(cur_argv, "-fsanitize=address") ||
+      !strcmp(cur_argv, "-fsanitize=memory")) {
 
-#endif
+    if (scan) {
+
+      // "-fsanitize=undefined,address" may be un-treated, but it's OK.
+      aflcc->asan_set = 1;
+      final_ = PARAM_SCAN;
+
+    } else {
 
-  cc_params[cc_par_cnt] = NULL;
+      // It's impossible that final_ is PARAM_DROP before,
+      // so no checks are needed here.
+      final_ = PARAM_KEEP;
+
+    }
+
+  }
+
+  if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv);
+
+  return final_;
 
 }
 
-/* Main entry point */
+void add_sanitizers(aflcc_state_t *aflcc, char **envp) {
 
-int main(int argc, char **argv, char **envp) {
+  if (!aflcc->asan_set) {
 
-  int   i;
-  char *callname = argv[0], *ptr = NULL;
+    if (getenv("AFL_USE_ASAN")) {
 
-  if (getenv("AFL_DEBUG")) {
+      if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
 
-    debug = 1;
-    if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG");
+      if (getenv("AFL_HARDEN"))
+        FATAL("ASAN and AFL_HARDEN are mutually exclusive");
 
-  } else if (getenv("AFL_QUIET"))
+      add_defs_fortify(aflcc, 0);
+      insert_param(aflcc, "-fsanitize=address");
 
-    be_quiet = 1;
+    } else if (getenv("AFL_USE_MSAN")) {
 
-  if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") ||
-      getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") ||
-      getenv("AFL_LLVM_BLOCKLIST")) {
+      if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
+
+      if (getenv("AFL_HARDEN"))
+        FATAL("MSAN and AFL_HARDEN are mutually exclusive");
+
+      add_defs_fortify(aflcc, 0);
+      insert_param(aflcc, "-fsanitize=memory");
 
-    have_instr_env = 1;
+    }
 
   }
 
-  if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) {
+  if (getenv("AFL_USE_UBSAN")) {
 
-    passthrough = 1;
-    if (!debug) { be_quiet = 1; }
+    insert_param(aflcc, "-fsanitize=undefined");
+    insert_param(aflcc, "-fsanitize-undefined-trap-on-error");
+    insert_param(aflcc, "-fno-sanitize-recover=all");
+    insert_param(aflcc, "-fno-omit-frame-pointer");
 
   }
 
-  if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1;
-  argvnull = (u8 *)argv[0];
-  check_environment_vars(envp);
+  if (getenv("AFL_USE_TSAN")) {
 
-  if ((ptr = find_object("as", argv[0])) != NULL) {
+    insert_param(aflcc, "-fsanitize=thread");
+    insert_param(aflcc, "-fno-omit-frame-pointer");
 
-    have_gcc = 1;
-    ck_free(ptr);
+  }
+
+  if (getenv("AFL_USE_LSAN")) {
+
+    insert_param(aflcc, "-fsanitize=leak");
+    add_defs_lsan_ctrl(aflcc);
 
   }
 
-#if (LLVM_MAJOR >= 3)
+  if (getenv("AFL_USE_CFISAN")) {
 
-  if ((ptr = find_object("SanitizerCoverageLTO.so", argv[0])) != NULL) {
+    if (aflcc->compiler_mode == GCC_PLUGIN || aflcc->compiler_mode == GCC) {
 
-    have_lto = 1;
-    ck_free(ptr);
+      insert_param(aflcc, "-fcf-protection=full");
 
-  }
+    } else {
 
-  if ((ptr = find_object("cmplog-routines-pass.so", argv[0])) != NULL) {
+      if (!aflcc->lto_mode) {
 
-    have_llvm = 1;
-    ck_free(ptr);
+        uint32_t i = 0, found = 0;
+        while (envp[i] != NULL && !found)
+          if (strncmp("-flto", envp[i++], 5) == 0) found = 1;
+        if (!found) insert_param(aflcc, "-flto");
+
+      }
+
+      insert_param(aflcc, "-fsanitize=cfi");
+      insert_param(aflcc, "-fvisibility=hidden");
+
+    }
 
   }
 
-#endif
+}
 
-#ifdef __ANDROID__
-  have_llvm = 1;
+void add_native_pcguard(aflcc_state_t *aflcc) {
+
+  /* If llvm-config doesn't figure out LLVM_MAJOR, just
+   go on anyway and let compiler complain if doesn't work. */
+
+  if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) {
+
+#if LLVM_MAJOR > 0 && LLVM_MAJOR < 6
+    FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+");
+#else
+  #if LLVM_MAJOR == 0
+    WARNF(
+        "pcguard instrumentation with pc-table requires LLVM 6.0.1+"
+        " otherwise the compiler will fail");
+  #endif
+    insert_param(aflcc,
+                 "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table");
 #endif
 
-  if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) {
+  } else {
 
-    have_gcc_plugin = 1;
-    ck_free(ptr);
+#if LLVM_MAJOR > 0 && LLVM_MAJOR < 4
+    FATAL("pcguard instrumentation requires LLVM 4.0.1+");
+#else
+  #if LLVM_MAJOR == 0
+    WARNF(
+        "pcguard instrumentation requires LLVM 4.0.1+"
+        " otherwise the compiler will fail");
+  #endif
+    insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
+#endif
 
   }
 
-#if (LLVM_MAJOR >= 3)
+}
+
+void add_optimized_pcguard(aflcc_state_t *aflcc) {
+
+#if LLVM_MAJOR >= 13
+  #if defined __ANDROID__ || ANDROID
 
-  if (strncmp(callname, "afl-clang-fast", 14) == 0) {
+  insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
+  aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-    compiler_mode = LLVM;
+  #else
 
-  } else if (strncmp(callname, "afl-clang-lto", 13) == 0 ||
+  if (aflcc->have_instr_list) {
 
-             strncmp(callname, "afl-lto", 7) == 0) {
+    if (!be_quiet)
+      SAYF(
+          "Using unoptimized trace-pc-guard, due usage of "
+          "-fsanitize-coverage-allow/denylist, you can use "
+          "AFL_LLVM_ALLOWLIST/AFL_LLMV_DENYLIST instead.\n");
 
-    compiler_mode = LTO;
+    insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
+    aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-  } else
+  } else {
 
-#endif
-      if (strncmp(callname, "afl-gcc-fast", 12) == 0 ||
+    /* Since LLVM_MAJOR >= 13 we use new pass manager */
+    #if LLVM_MAJOR < 16
+    insert_param(aflcc, "-fexperimental-new-pass-manager");
+    #endif
+    insert_object(aflcc, "SanitizerCoveragePCGUARD.so", "-fpass-plugin=%s", 0);
+
+  }
+
+  #endif  // defined __ANDROID__ || ANDROID
+#else     // LLVM_MAJOR < 13
+  #if LLVM_MAJOR >= 4
+
+  if (!be_quiet)
+    SAYF(
+        "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for "
+        "enhanced version.\n");
+  insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
+  aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-          strncmp(callname, "afl-g++-fast", 12) == 0) {
+  #else
 
-    compiler_mode = GCC_PLUGIN;
+  FATAL("pcguard instrumentation requires LLVM 4.0.1+");
 
-  } else if (strncmp(callname, "afl-gcc", 7) == 0 ||
+  #endif
+#endif
 
-             strncmp(callname, "afl-g++", 7) == 0) {
+}
 
-    compiler_mode = GCC;
+/* Linking behaviors */
 
-  } else if (strcmp(callname, "afl-clang") == 0 ||
+param_st parse_linking_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan,
+                              u8 *skip_next, char **argv) {
 
-             strcmp(callname, "afl-clang++") == 0) {
+  if (aflcc->lto_mode && !strncmp(cur_argv, "-flto=thin", 10)) {
 
-    compiler_mode = CLANG;
+    FATAL(
+        "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or "
+        "use afl-clang-fast!");
 
   }
 
-  if ((ptr = getenv("AFL_CC_COMPILER"))) {
+  param_st final_ = PARAM_MISS;
 
-    if (compiler_mode) {
+  if (!strcmp(cur_argv, "-shared") || !strcmp(cur_argv, "-dynamiclib")) {
 
-      if (!be_quiet) {
+    if (scan) {
 
-        WARNF(
-            "\"AFL_CC_COMPILER\" is set but a specific compiler was already "
-            "selected by command line parameter or symlink, ignoring the "
-            "environment variable!");
+      aflcc->shared_linking = 1;
+      final_ = PARAM_SCAN;
 
-      }
+    } else {
+
+      final_ = PARAM_KEEP;
+
+    }
+
+  } else if (!strcmp(cur_argv, "-Wl,-r") || !strcmp(cur_argv, "-Wl,-i") ||
+
+             !strcmp(cur_argv, "-Wl,--relocatable") ||
+             !strcmp(cur_argv, "-r") || !strcmp(cur_argv, "--relocatable")) {
+
+    if (scan) {
+
+      aflcc->partial_linking = 1;
+      final_ = PARAM_SCAN;
 
     } else {
 
-      if (strncasecmp(ptr, "LTO", 3) == 0) {
+      final_ = PARAM_KEEP;
 
-        compiler_mode = LTO;
+    }
 
-      } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
+  } else if (!strncmp(cur_argv, "-fuse-ld=", 9) ||
 
-        compiler_mode = LLVM;
+             !strncmp(cur_argv, "--ld-path=", 10)) {
 
-      } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
+    if (scan) {
 
-                 strncasecmp(ptr, "GCC-P", 5) == 0 ||
-                 strncasecmp(ptr, "GCCP", 4) == 0) {
+      final_ = PARAM_SCAN;
 
-        compiler_mode = GCC_PLUGIN;
+    } else {
 
-      } else if (strcasecmp(ptr, "GCC") == 0) {
+      if (aflcc->lto_mode)
+        final_ = PARAM_DROP;
+      else
+        final_ = PARAM_KEEP;
 
-        compiler_mode = GCC;
+    }
 
-      } else
+  } else if (!strcmp(cur_argv, "-Wl,-z,defs") ||
+
+             !strcmp(cur_argv, "-Wl,--no-undefined") ||
+             !strcmp(cur_argv, "-Wl,-no-undefined") ||
+             !strcmp(cur_argv, "--no-undefined") ||
+             strstr(cur_argv, "afl-compiler-rt") ||
+             strstr(cur_argv, "afl-llvm-rt")) {
+
+    if (scan) {
+
+      final_ = PARAM_SCAN;
+
+    } else {
 
-        FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr);
+      final_ = PARAM_DROP;
 
     }
 
-  }
+  } else if (!strcmp(cur_argv, "-z") || !strcmp(cur_argv, "-Wl,-z")) {
+
+    u8 *param = *(argv + 1);
+    if (param && (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs"))) {
+
+      *skip_next = 1;
 
-  if (strcmp(callname, "afl-clang") == 0 ||
-      strcmp(callname, "afl-clang++") == 0) {
+      if (scan) {
 
-    clang_mode = 1;
-    compiler_mode = CLANG;
+        final_ = PARAM_SCAN;
 
-    if (strcmp(callname, "afl-clang++") == 0) { plusplus_mode = 1; }
+      } else {
+
+        final_ = PARAM_DROP;
+
+      }
+
+    }
 
   }
 
-  for (i = 1; i < argc; i++) {
+  // Try to warn user for some unsupported cases
+  if (scan && final_ == PARAM_MISS) {
 
-    if (strncmp(argv[i], "--afl", 5) == 0) {
+    u8 *ptr_ = NULL;
 
-      if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) {
+    if (!strcmp(cur_argv, "-Xlinker") && (ptr_ = *(argv + 1))) {
 
-        passthrough = 1;
-        argv[i] = "-g";  // we have to overwrite it, -g is always good
-        continue;
+      if (!strcmp(ptr_, "defs")) {
 
-      }
+        WARNF("'-Xlinker' 'defs' detected. This may result in a bad link.");
 
-      if (compiler_mode && !be_quiet) {
+      } else if (strstr(ptr_, "-no-undefined")) {
 
         WARNF(
-            "--afl-... compiler mode supersedes the AFL_CC_COMPILER and "
-            "symlink compiler selection!");
+            "'-Xlinker' '%s' detected. The latter option may be dropped and "
+            "result in a bad link.",
+            ptr_);
 
       }
 
-      ptr = argv[i];
-      ptr += 5;
-      while (*ptr == '-')
-        ptr++;
+    } else if (!strncmp(cur_argv, "-Wl,", 4) &&
 
-      if (strncasecmp(ptr, "LTO", 3) == 0) {
+               (u8 *)strrchr(cur_argv, ',') != (cur_argv + 3)) {
 
-        compiler_mode = LTO;
+      ptr_ = cur_argv + 4;
 
-      } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
+      if (strstr(ptr_, "-shared") || strstr(ptr_, "-dynamiclib")) {
 
-        compiler_mode = LLVM;
+        WARNF(
+            "'%s': multiple link options after '-Wl,' may break shared "
+            "linking.",
+            ptr_);
 
-      } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 ||
+      }
 
-                 strncasecmp(ptr, "PC-GUARD", 8) == 0) {
+      if (strstr(ptr_, "-r,") || strstr(ptr_, "-i,") || strstr(ptr_, ",-r") ||
+          strstr(ptr_, ",-i") || strstr(ptr_, "--relocatable")) {
 
-        compiler_mode = LLVM;
-        instrument_mode = INSTRUMENT_PCGUARD;
+        WARNF(
+            "'%s': multiple link options after '-Wl,' may break partial "
+            "linking.",
+            ptr_);
 
-      } else if (strcasecmp(ptr, "INSTRIM") == 0 ||
+      }
 
-                 strcasecmp(ptr, "CFG") == 0) {
+      if (strstr(ptr_, "defs") || strstr(ptr_, "no-undefined")) {
 
-        FATAL(
-            "InsTrim instrumentation was removed. Use a modern LLVM and "
-            "PCGUARD (default in afl-cc).\n");
+        WARNF(
+            "'%s': multiple link options after '-Wl,' may enable report "
+            "unresolved symbol references and result in a bad link.",
+            ptr_);
 
-      } else if (strcasecmp(ptr, "AFL") == 0 ||
+      }
 
-                 strcasecmp(ptr, "CLASSIC") == 0) {
+    }
 
-        compiler_mode = LLVM;
-        instrument_mode = INSTRUMENT_CLASSIC;
+  }
 
-      } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 ||
+  if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv);
 
-                 strcasecmp(ptr, "NATIVE") == 0 ||
-                 strcasecmp(ptr, "LLVM-NATIVE") == 0) {
+  return final_;
 
-        compiler_mode = LLVM;
-        instrument_mode = INSTRUMENT_LLVMNATIVE;
+}
 
-      } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
+void add_lto_linker(aflcc_state_t *aflcc) {
 
-                 strncasecmp(ptr, "GCC-P", 5) == 0 ||
-                 strncasecmp(ptr, "GCCP", 4) == 0) {
+  unsetenv("AFL_LD");
+  unsetenv("AFL_LD_CALLER");
 
-        compiler_mode = GCC_PLUGIN;
+  u8 *ld_path = NULL;
+  if (getenv("AFL_REAL_LD")) {
 
-      } else if (strcasecmp(ptr, "GCC") == 0) {
+    ld_path = strdup(getenv("AFL_REAL_LD"));
 
-        compiler_mode = GCC;
+  } else {
 
-      } else if (strncasecmp(ptr, "CLANG", 5) == 0) {
+    ld_path = strdup(AFL_REAL_LD);
 
-        compiler_mode = CLANG;
+  }
 
-      } else
+  if (!ld_path || !*ld_path) {
 
-        FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]);
+    if (ld_path) {
+
+      // Freeing empty string
+      free(ld_path);
 
     }
 
+    ld_path = strdup("ld.lld");
+
   }
 
-  if (strlen(callname) > 2 &&
-      (strncmp(callname + strlen(callname) - 2, "++", 2) == 0 ||
-       strstr(callname, "-g++") != NULL))
-    plusplus_mode = 1;
+  if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); }
+#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12
+  insert_param(aflcc, alloc_printf("--ld-path=%s", ld_path));
+#else
+  insert_param(aflcc, alloc_printf("-fuse-ld=%s", ld_path));
+#endif
+  free(ld_path);
+
+}
 
-  if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
-      getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
+void add_lto_passes(aflcc_state_t *aflcc) {
 
-    if (instrument_mode == 0)
-      instrument_mode = INSTRUMENT_PCGUARD;
-    else if (instrument_mode != INSTRUMENT_PCGUARD)
-      FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together");
+#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15
+  // The NewPM implementation only works fully since LLVM 15.
+  insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,--load-pass-plugin=%s",
+                0);
+#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13
+  insert_param(aflcc, "-Wl,--lto-legacy-pass-manager");
+  insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0);
+#else
+  insert_param(aflcc, "-fno-experimental-new-pass-manager");
+  insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0);
+#endif
 
-  }
+  insert_param(aflcc, "-Wl,--allow-multiple-definition");
 
-  if (have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) {
+}
 
-    WARNF(
-        "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined "
-        "for file matching, only function matching!");
+static void add_aflpplib(aflcc_state_t *aflcc) {
 
-  }
+  if (!aflcc->need_aflpplib) return;
 
-  if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") ||
-      getenv("INSTRIM_LIB")) {
+  u8 *afllib = find_object(aflcc, "libAFLDriver.a");
 
-    FATAL(
-        "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD "
-        "(default in afl-cc).\n");
+  if (!be_quiet) {
+
+    OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
 
   }
 
-  if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX;
-  if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+  if (!afllib) {
 
-  if (getenv("AFL_LLVM_NGRAM_SIZE")) {
+    if (!be_quiet) {
 
-    instrument_opt_mode |= INSTRUMENT_OPT_NGRAM;
-    ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE"));
-    if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX)
-      FATAL(
-          "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX "
-          "(%u)",
-          NGRAM_SIZE_MAX);
+      WARNF(
+          "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
+          "the flags - this will fail!");
 
-  }
+    }
 
-  if (getenv("AFL_LLVM_CTX_K")) {
+  } else {
 
-    ctx_k = atoi(getenv("AFL_LLVM_CTX_K"));
-    if (ctx_k < 1 || ctx_k > CTX_MAX_K)
-      FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)",
-            CTX_MAX_K);
-    if (ctx_k == 1) {
+    insert_param(aflcc, afllib);
 
-      setenv("AFL_LLVM_CALLER", "1", 1);
-      unsetenv("AFL_LLVM_CTX_K");
-      instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+#ifdef __APPLE__
+    insert_param(aflcc, "-Wl,-undefined");
+    insert_param(aflcc, "dynamic_lookup");
+#endif
 
-    } else {
+  }
 
-      instrument_opt_mode |= INSTRUMENT_OPT_CTX_K;
+}
 
-    }
+void add_runtime(aflcc_state_t *aflcc) {
+
+  if (aflcc->preprocessor_only || aflcc->have_c || !aflcc->non_dash) {
+
+    /* In the preprocessor_only case (-E), we are not actually compiling at
+       all but requesting the compiler to output preprocessed sources only.
+       We must not add the runtime in this case because the compiler will
+       simply output its binary content back on stdout, breaking any build
+       systems that rely on a separate source preprocessing step. */
+    return;
 
   }
 
-  if (getenv("AFL_LLVM_INSTRUMENT")) {
+  if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC &&
+      !getenv("AFL_LLVM_NO_RPATH")) {
 
-    u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;");
+    // in case LLVM is installed not via a package manager or "make install"
+    // e.g. compiled download or compiled from github then its ./lib directory
+    // might not be in the search path. Add it if so.
+    const char *libdir = LLVM_LIBDIR;
+    if (aflcc->plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) &&
+        strncmp(libdir, "/lib", 4)) {
 
-    while (ptr2) {
+      u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR);
+      insert_param(aflcc, libdir_opt);
 
-      if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 ||
-          strncasecmp(ptr2, "classic", strlen("classic")) == 0) {
+    }
 
-        if (instrument_mode == INSTRUMENT_LTO) {
+  }
 
-          instrument_mode = INSTRUMENT_CLASSIC;
-          lto_mode = 1;
+#ifndef __ANDROID__
 
-        } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) {
+  #define M32_ERR_MSG "-m32 is not supported by your compiler"
+  #define M64_ERR_MSG "-m64 is not supported by your compiler"
 
-          instrument_mode = INSTRUMENT_AFL;
+  if (aflcc->compiler_mode != GCC && aflcc->compiler_mode != CLANG) {
 
-        } else {
+    switch (aflcc->bit_mode) {
 
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+      case 0:
+        if (!aflcc->shared_linking && !aflcc->partial_linking)
+          insert_object(aflcc, "afl-compiler-rt.o", 0, 0);
+        if (aflcc->lto_mode) insert_object(aflcc, "afl-llvm-rt-lto.o", 0, 0);
+        break;
 
-        }
+      case 32:
+        if (!aflcc->shared_linking && !aflcc->partial_linking)
+          insert_object(aflcc, "afl-compiler-rt-32.o", 0, M32_ERR_MSG);
+        if (aflcc->lto_mode)
+          insert_object(aflcc, "afl-llvm-rt-lto-32.o", 0, M32_ERR_MSG);
+        break;
 
-      }
+      case 64:
+        if (!aflcc->shared_linking && !aflcc->partial_linking)
+          insert_object(aflcc, "afl-compiler-rt-64.o", 0, M64_ERR_MSG);
+        if (aflcc->lto_mode)
+          insert_object(aflcc, "afl-llvm-rt-lto-64.o", 0, M64_ERR_MSG);
+        break;
 
-      if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 ||
-          strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) {
+    }
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD)
-          instrument_mode = INSTRUMENT_PCGUARD;
-        else
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+  #if !defined(__APPLE__) && !defined(__sun)
+    if (!aflcc->shared_linking && !aflcc->partial_linking)
+      insert_object(aflcc, "dynamic_list.txt", "-Wl,--dynamic-list=%s", 0);
+  #endif
 
-      }
+  #if defined(__APPLE__)
+    if (aflcc->shared_linking || aflcc->partial_linking) {
 
-      if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 ||
-          strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 ||
-          strncasecmp(ptr2, "native", strlen("native")) == 0) {
+      insert_param(aflcc, "-Wl,-U");
+      insert_param(aflcc, "-Wl,___afl_area_ptr");
+      insert_param(aflcc, "-Wl,-U");
+      insert_param(aflcc, "-Wl,___sanitizer_cov_trace_pc_guard_init");
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE)
-          instrument_mode = INSTRUMENT_LLVMNATIVE;
-        else
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+    }
 
-      }
+  #endif
 
-      if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 ||
-          strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) {
+  }
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) {
+#endif
 
-          instrument_mode = INSTRUMENT_LLVMNATIVE;
-          instrument_opt_mode |= INSTRUMENT_OPT_CODECOV;
+  add_aflpplib(aflcc);
 
-        } else {
+#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__
+  insert_param(aflcc, "-Wl,-lrt");
+#endif
 
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+}
 
-        }
+/* Misc */
 
-      }
+void add_assembler(aflcc_state_t *aflcc) {
 
-      if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 ||
-          strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) {
+  u8 *afl_as = find_object(aflcc, "as");
 
-        FATAL(
-            "InsTrim instrumentation was removed. Use a modern LLVM and "
-            "PCGUARD (default in afl-cc).\n");
+  if (!afl_as) FATAL("Cannot find 'as' (symlink to 'afl-as').");
 
-      }
+  u8 *slash = strrchr(afl_as, '/');
+  if (slash) *slash = 0;
 
-      if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) {
+  insert_param(aflcc, "-B");
+  insert_param(aflcc, afl_as);
 
-        lto_mode = 1;
-        if (!instrument_mode || instrument_mode == INSTRUMENT_LTO)
-          instrument_mode = INSTRUMENT_LTO;
-        else
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+  if (aflcc->compiler_mode == CLANG) insert_param(aflcc, "-no-integrated-as");
 
-      }
+}
 
-      if (strcasecmp(ptr2, "gcc") == 0) {
+void add_gcc_plugin(aflcc_state_t *aflcc) {
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_GCC)
-          instrument_mode = INSTRUMENT_GCC;
-        else if (instrument_mode != INSTRUMENT_GCC)
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
-        compiler_mode = GCC;
+  if (aflcc->cmplog_mode) {
 
-      }
+    insert_object(aflcc, "afl-gcc-cmplog-pass.so", "-fplugin=%s", 0);
+    insert_object(aflcc, "afl-gcc-cmptrs-pass.so", "-fplugin=%s", 0);
 
-      if (strcasecmp(ptr2, "clang") == 0) {
+  }
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG)
-          instrument_mode = INSTRUMENT_CLANG;
-        else if (instrument_mode != INSTRUMENT_CLANG)
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
-        compiler_mode = CLANG;
+  insert_object(aflcc, "afl-gcc-pass.so", "-fplugin=%s", 0);
 
-      }
+  insert_param(aflcc, "-fno-if-conversion");
+  insert_param(aflcc, "-fno-if-conversion2");
 
-      if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 ||
-          strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 ||
-          strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) {
+}
 
-        u8 *ptr3 = ptr2;
-        while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9'))
-          ptr3++;
+void add_misc_params(aflcc_state_t *aflcc) {
 
-        if (!*ptr3) {
+  if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
+      getenv("AFL_LLVM_LAF_ALL") || getenv("AFL_LLVM_CMPLOG") ||
+      aflcc->lto_mode) {
 
-          if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL)
-            FATAL(
-                "you must set the K-CTX K with (e.g. for value 2) "
-                "AFL_LLVM_INSTRUMENT=ctx-2");
+    insert_param(aflcc, "-fno-builtin-strcmp");
+    insert_param(aflcc, "-fno-builtin-strncmp");
+    insert_param(aflcc, "-fno-builtin-strcasecmp");
+    insert_param(aflcc, "-fno-builtin-strncasecmp");
+    insert_param(aflcc, "-fno-builtin-memcmp");
+    insert_param(aflcc, "-fno-builtin-bcmp");
+    insert_param(aflcc, "-fno-builtin-strstr");
+    insert_param(aflcc, "-fno-builtin-strcasestr");
 
-        }
+  }
 
-        ctx_k = atoi(ptr3);
-        if (ctx_k < 1 || ctx_k > CTX_MAX_K)
-          FATAL(
-              "K-CTX instrumentation option must be between 1 and CTX_MAX_K "
-              "(%u)",
-              CTX_MAX_K);
+  if (!aflcc->have_pic) { insert_param(aflcc, "-fPIC"); }
 
-        if (ctx_k == 1) {
+  if (getenv("AFL_HARDEN")) {
 
-          instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
-          setenv("AFL_LLVM_CALLER", "1", 1);
-          unsetenv("AFL_LLVM_CTX_K");
+    insert_param(aflcc, "-fstack-protector-all");
 
-        } else {
+    if (!aflcc->fortify_set) add_defs_fortify(aflcc, 2);
 
-          instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K);
-          u8 *ptr4 = alloc_printf("%u", ctx_k);
-          setenv("AFL_LLVM_CTX_K", ptr4, 1);
+  }
 
-        }
+  if (!getenv("AFL_DONT_OPTIMIZE")) {
 
-      }
+    insert_param(aflcc, "-g");
+    if (!aflcc->have_o) insert_param(aflcc, "-O3");
+    if (!aflcc->have_unroll) insert_param(aflcc, "-funroll-loops");
+    // if (strlen(aflcc->march_opt) > 1 && aflcc->march_opt[0] == '-')
+    //     insert_param(aflcc, aflcc->march_opt);
 
-      if (strcasecmp(ptr2, "ctx") == 0) {
+  }
 
-        instrument_opt_mode |= INSTRUMENT_OPT_CTX;
-        setenv("AFL_LLVM_CTX", "1", 1);
+  if (aflcc->x_set) {
 
-      }
+    insert_param(aflcc, "-x");
+    insert_param(aflcc, "none");
 
-      if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) {
+  }
 
-        instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
-        setenv("AFL_LLVM_CALLER", "1", 1);
+}
 
-      }
+param_st parse_misc_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) {
 
-      if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) {
+  param_st final_ = PARAM_MISS;
 
-        u8 *ptr3 = ptr2 + strlen("ngram");
-        while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9'))
-          ptr3++;
+// MACRO START
+#define SCAN_KEEP(dst, src) \
+  do {                      \
+                            \
+    if (scan) {             \
+                            \
+      dst = src;            \
+      final_ = PARAM_SCAN;  \
+                            \
+    } else {                \
+                            \
+      final_ = PARAM_KEEP;  \
+                            \
+    }                       \
+                            \
+  } while (0)
 
-        if (!*ptr3) {
+  // MACRO END
 
-          if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL)
-            FATAL(
-                "you must set the NGRAM size with (e.g. for value 2) "
-                "AFL_LLVM_INSTRUMENT=ngram-2");
+  if (!strncasecmp(cur_argv, "-fpic", 5)) {
 
-        }
+    SCAN_KEEP(aflcc->have_pic, 1);
 
-        ngram_size = atoi(ptr3);
-        if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX)
-          FATAL(
-              "NGRAM instrumentation option must be between 2 and "
-              "NGRAM_SIZE_MAX (%u)",
-              NGRAM_SIZE_MAX);
-        instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM);
-        u8 *ptr4 = alloc_printf("%u", ngram_size);
-        setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1);
+  } else if (!strcmp(cur_argv, "-m32") ||
 
-      }
+             !strcmp(cur_argv, "armv7a-linux-androideabi")) {
 
-      ptr2 = strtok(NULL, ":,;");
+    SCAN_KEEP(aflcc->bit_mode, 32);
 
-    }
+  } else if (!strcmp(cur_argv, "-m64")) {
 
-  }
+    SCAN_KEEP(aflcc->bit_mode, 64);
 
-  if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
-      (instrument_opt_mode & INSTRUMENT_OPT_CALLER)) {
+  } else if (strstr(cur_argv, "FORTIFY_SOURCE")) {
 
-    FATAL("you cannot set CTX and CALLER together");
+    SCAN_KEEP(aflcc->fortify_set, 1);
 
-  }
+  } else if (!strcmp(cur_argv, "-x")) {
 
-  if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
-      (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
+    SCAN_KEEP(aflcc->x_set, 1);
 
-    FATAL("you cannot set CTX and K-CTX together");
+  } else if (!strcmp(cur_argv, "-E")) {
 
-  }
+    SCAN_KEEP(aflcc->preprocessor_only, 1);
 
-  if ((instrument_opt_mode & INSTRUMENT_OPT_CALLER) &&
-      (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
+  } else if (!strcmp(cur_argv, "--target=wasm32-wasi")) {
 
-    FATAL("you cannot set CALLER and K-CTX together");
+    SCAN_KEEP(aflcc->passthrough, 1);
 
-  }
+  } else if (!strcmp(cur_argv, "-c")) {
 
-  if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT &&
-      (compiler_mode == LLVM || compiler_mode == UNSET)) {
+    SCAN_KEEP(aflcc->have_c, 1);
 
-    instrument_mode = INSTRUMENT_CLASSIC;
-    compiler_mode = LLVM;
+  } else if (!strncmp(cur_argv, "-O", 2)) {
 
-  }
+    SCAN_KEEP(aflcc->have_o, 1);
 
-  if (!compiler_mode) {
+  } else if (!strncmp(cur_argv, "-funroll-loop", 13)) {
 
-    // lto is not a default because outside of afl-cc RANLIB and AR have to
-    // be set to LLVM versions so this would work
-    if (have_llvm)
-      compiler_mode = LLVM;
-    else if (have_gcc_plugin)
-      compiler_mode = GCC_PLUGIN;
-    else if (have_gcc)
-#ifdef __APPLE__
-      // on OSX clang masquerades as GCC
-      compiler_mode = CLANG;
-#else
-      compiler_mode = GCC;
-#endif
-    else if (have_lto)
-      compiler_mode = LTO;
+    SCAN_KEEP(aflcc->have_unroll, 1);
+
+  } else if (!strncmp(cur_argv, "--afl", 5)) {
+
+    if (scan)
+      final_ = PARAM_SCAN;
     else
-      FATAL("no compiler mode available");
+      final_ = PARAM_DROP;
 
-  }
+  } else if (!strncmp(cur_argv, "-fno-unroll", 11)) {
 
-  /* if our PCGUARD implementation is not available then silently switch to
-     native LLVM PCGUARD */
-  if (compiler_mode == CLANG &&
-      (instrument_mode == INSTRUMENT_DEFAULT ||
-       instrument_mode == INSTRUMENT_PCGUARD) &&
-      find_object("SanitizerCoveragePCGUARD.so", argv[0]) == NULL) {
+    if (scan)
+      final_ = PARAM_SCAN;
+    else
+      final_ = PARAM_DROP;
 
-    instrument_mode = INSTRUMENT_LLVMNATIVE;
+  } else if (!strcmp(cur_argv, "-pipe") && aflcc->compiler_mode == GCC_PLUGIN) {
 
-  }
+    if (scan)
+      final_ = PARAM_SCAN;
+    else
+      final_ = PARAM_DROP;
 
-  if (compiler_mode == GCC) {
+  } else if (!strncmp(cur_argv, "-stdlib=", 8) &&
 
-    if (clang_mode) {
+             (aflcc->compiler_mode == GCC ||
+              aflcc->compiler_mode == GCC_PLUGIN)) {
 
-      instrument_mode = INSTRUMENT_CLANG;
+    if (scan) {
+
+      final_ = PARAM_SCAN;
 
     } else {
 
-      instrument_mode = INSTRUMENT_GCC;
+      if (!be_quiet) WARNF("Found '%s' - stripping!", cur_argv);
+      final_ = PARAM_DROP;
 
     }
 
-  }
+  } else if (cur_argv[0] != '-') {
 
-  if (compiler_mode == CLANG) {
+    /* It's a weak, loose pattern, with very different purpose
+     than others. We handle it at last, cautiously and robustly. */
 
-    instrument_mode = INSTRUMENT_CLANG;
-    setenv(CLANG_ENV_VAR, "1", 1);  // used by afl-as
+    if (scan && cur_argv[0] != '@')  // response file support
+      aflcc->non_dash = 1;
 
   }
 
+#undef SCAN_KEEP
+
+  if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv);
+
+  return final_;
+
+}
+
+static void maybe_usage(aflcc_state_t *aflcc, int argc, char **argv) {
+
   if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) {
 
     printf("afl-cc" VERSION
@@ -2168,16 +2559,22 @@ int main(int argc, char **argv, char **envp) {
         "  [GCC/CLANG] simple gcc/clang: %s%s\n"
         "      CLASSIC              DEFAULT      no  no      no     no  no     "
         "no\n\n",
-        have_llvm ? "AVAILABLE" : "unavailable!",
-        compiler_mode == LLVM ? " [SELECTED]" : "",
-        have_llvm ? "AVAILABLE" : "unavailable!",
-        have_llvm ? "AVAILABLE" : "unavailable!",
-        have_lto ? "AVAILABLE" : "unavailable!",
-        compiler_mode == LTO ? " [SELECTED]" : "",
-        have_gcc_plugin ? "AVAILABLE" : "unavailable!",
-        compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "",
-        have_gcc ? "AVAILABLE" : "unavailable!",
-        (compiler_mode == GCC || compiler_mode == CLANG) ? " [SELECTED]" : "");
+        aflcc->have_llvm ? "AVAILABLE" : "unavailable!",
+        aflcc->compiler_mode == LLVM ? " [SELECTED]" : "",
+        aflcc->have_llvm ? "AVAILABLE" : "unavailable!",
+        aflcc->have_llvm ? "AVAILABLE" : "unavailable!",
+        aflcc->have_lto ? "AVAILABLE" : "unavailable!",
+        aflcc->compiler_mode == LTO ? " [SELECTED]" : "",
+        aflcc->have_gcc_plugin ? "AVAILABLE" : "unavailable!",
+        aflcc->compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "",
+        aflcc->have_gcc && aflcc->have_clang
+            ? "AVAILABLE"
+            : (aflcc->have_gcc
+                   ? "GCC ONLY "
+                   : (aflcc->have_clang ? "CLANG ONLY" : "unavailable!")),
+        (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG)
+            ? " [SELECTED]"
+            : "");
 
     SAYF(
         "Modes:\n"
@@ -2266,7 +2663,7 @@ int main(int argc, char **argv, char **envp) {
           "  AFL_USE_TSAN: activate thread sanitizer\n"
           "  AFL_USE_LSAN: activate leak-checker sanitizer\n");
 
-      if (have_gcc_plugin)
+      if (aflcc->have_gcc_plugin)
         SAYF(
             "\nGCC Plugin-specific environment variables:\n"
             "  AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n"
@@ -2282,7 +2679,7 @@ int main(int argc, char **argv, char **envp) {
   #define COUNTER_BEHAVIOUR \
     "  AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n"
 #endif
-      if (have_llvm)
+      if (aflcc->have_llvm)
         SAYF(
             "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment "
             "variables:\n"
@@ -2310,7 +2707,7 @@ int main(int argc, char **argv, char **envp) {
             "instrument allow/\n"
             "    deny listing (selective instrumentation)\n");
 
-      if (have_llvm)
+      if (aflcc->have_llvm)
         SAYF(
             "  AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen "
             "mutator)\n"
@@ -2329,7 +2726,7 @@ int main(int argc, char **argv, char **envp) {
             "locations\n");
 
 #ifdef AFL_CLANG_FLTO
-      if (have_lto)
+      if (aflcc->have_lto)
         SAYF(
             "\nLTO/afl-clang-lto specific environment variables:\n"
             "  AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), "
@@ -2365,9 +2762,9 @@ int main(int argc, char **argv, char **envp) {
         "targets.\n\n");
 
 #if (LLVM_MAJOR >= 3)
-    if (have_lto)
+    if (aflcc->have_lto)
       SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO);
-    if (have_llvm)
+    if (aflcc->have_llvm)
       SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR,
            LLVM_BINDIR);
 #endif
@@ -2406,205 +2803,356 @@ int main(int argc, char **argv, char **envp) {
 
   }
 
-  if (compiler_mode == LTO) {
-
-    if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO ||
-        instrument_mode == INSTRUMENT_CFG ||
-        instrument_mode == INSTRUMENT_PCGUARD) {
+}
 
-      lto_mode = 1;
-      // force CFG
-      // if (!instrument_mode) {
+static void process_params(aflcc_state_t *aflcc, u8 scan, u32 argc,
+                           char **argv) {
 
-      instrument_mode = INSTRUMENT_PCGUARD;
-      // ptr = instrument_mode_string[instrument_mode];
-      // }
+  limit_params(aflcc, argc);
 
-    } else if (instrument_mode == INSTRUMENT_CLASSIC) {
+  // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]);
 
-      lto_mode = 1;
+  /* Process the argument list. */
 
-    } else {
+  u8 skip_next = 0;
+  while (--argc) {
 
-      if (!be_quiet) {
+    u8 *cur = *(++argv);
 
-        WARNF("afl-clang-lto called with mode %s, using that mode instead",
-              instrument_mode_string[instrument_mode]);
+    if (skip_next > 0) {
 
-      }
+      skip_next--;
+      continue;
 
     }
 
-  }
+    if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue;
 
-  if (instrument_mode == 0 && compiler_mode < GCC_PLUGIN) {
+    if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue;
 
-#if LLVM_MAJOR >= 7
-  #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
-    if (have_instr_env) {
+    if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv))
+      continue;
 
-      instrument_mode = INSTRUMENT_AFL;
-      if (!be_quiet) {
+    if (*cur == '@') {
 
-        WARNF(
-            "Switching to classic instrumentation because "
-            "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1.");
+      // response file support.
+      // we have two choices - move everything to the command line or
+      // rewrite the response files to temporary files and delete them
+      // afterwards. We choose the first for easiness.
+      // We do *not* support quotes in the rsp files to cope with spaces in
+      // filenames etc! If you need that then send a patch!
+      u8 *filename = cur + 1;
+      if (aflcc->debug) { DEBUGF("response file=%s\n", filename); }
+      FILE       *f = fopen(filename, "r");
+      struct stat st;
+
+      // Check not found or empty? let the compiler complain if so.
+      if (!f || fstat(fileno(f), &st) < 0 || st.st_size < 1) {
+
+        if (!scan) insert_param(aflcc, cur);
+        continue;
 
       }
 
-    } else
+      u8    *tmpbuf = malloc(st.st_size + 2), *ptr;
+      char **args = malloc(sizeof(char *) * (st.st_size >> 1));
+      int    count = 1, cont = 0, cont_act = 0;
 
-  #endif
-      instrument_mode = INSTRUMENT_PCGUARD;
+      while (fgets(tmpbuf, st.st_size + 1, f)) {
 
-#else
-    instrument_mode = INSTRUMENT_AFL;
-#endif
+        ptr = tmpbuf;
+        // fprintf(stderr, "1: %s\n", ptr);
+        //  no leading whitespace
+        while (isspace(*ptr)) {
 
-  }
+          ++ptr;
+          cont_act = 0;
 
-  if (instrument_opt_mode && compiler_mode != LLVM)
-    FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode");
+        }
 
-  if (!instrument_opt_mode) {
+        // no comments, no empty lines
+        if (*ptr == '#' || *ptr == '\n' || !*ptr) { continue; }
+        // remove LF
+        if (ptr[strlen(ptr) - 1] == '\n') { ptr[strlen(ptr) - 1] = 0; }
+        // remove CR
+        if (*ptr && ptr[strlen(ptr) - 1] == '\r') { ptr[strlen(ptr) - 1] = 0; }
+        // handle \ at end of line
+        if (*ptr && ptr[strlen(ptr) - 1] == '\\') {
 
-    if (lto_mode && instrument_mode == INSTRUMENT_CFG)
-      instrument_mode = INSTRUMENT_PCGUARD;
-    ptr = instrument_mode_string[instrument_mode];
+          cont = 1;
+          ptr[strlen(ptr) - 1] = 0;
 
-  } else {
+        }
 
-    char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size);
-    char *ptr3 = alloc_printf(" + K-CTX-%u", ctx_k);
+        // fprintf(stderr, "2: %s\n", ptr);
 
-    ptr = alloc_printf(
-        "%s%s%s%s%s", instrument_mode_string[instrument_mode],
-        (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "",
-        (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "",
-        (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "",
-        (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "");
+        // remove whitespace at end
+        while (*ptr && isspace(ptr[strlen(ptr) - 1])) {
 
-    ck_free(ptr2);
-    ck_free(ptr3);
+          ptr[strlen(ptr) - 1] = 0;
+          cont = 0;
 
-  }
+        }
 
-#ifndef AFL_CLANG_FLTO
-  if (lto_mode)
-    FATAL(
-        "instrumentation mode LTO specified but LLVM support not available "
-        "(requires LLVM 11 or higher)");
-#endif
+        // fprintf(stderr, "3: %s\n", ptr);
+        if (*ptr) {
 
-  if (instrument_opt_mode && instrument_opt_mode != INSTRUMENT_OPT_CODECOV &&
-      instrument_mode != INSTRUMENT_CLASSIC)
-    FATAL(
-        "CALLER, CTX and NGRAM instrumentation options can only be used with "
-        "the LLVM CLASSIC instrumentation mode.");
+          do {
 
-  if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO"))
-    FATAL(
-        "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set "
-        "together");
+            u8 *value = ptr;
+            while (*ptr && !isspace(*ptr)) {
 
-#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
-  if (instrument_mode == INSTRUMENT_PCGUARD && have_instr_env) {
+              ++ptr;
 
-    FATAL(
-        "Instrumentation type PCGUARD does not support "
-        "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead.");
+            }
 
-  }
+            while (*ptr && isspace(*ptr)) {
 
-#endif
+              *ptr++ = 0;
 
-  u8 *ptr2;
+            }
 
-  if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/')
-    FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path");
+            if (cont_act) {
 
-  if ((isatty(2) && !be_quiet) || debug) {
+              u32 len = strlen(args[count - 1]) + strlen(value) + 1;
+              u8 *tmp = malloc(len);
+              snprintf(tmp, len, "%s%s", args[count - 1], value);
+              free(args[count - 1]);
+              args[count - 1] = tmp;
+              cont_act = 0;
 
-    SAYF(cCYA
-         "afl-cc" VERSION cRST
-         " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n",
-         compiler_mode_string[compiler_mode], ptr);
+            } else {
 
-  }
+              args[count++] = strdup(value);
 
-  if (!be_quiet && (compiler_mode == GCC || compiler_mode == CLANG)) {
+            }
 
-    WARNF(
-        "You are using outdated instrumentation, install LLVM and/or "
-        "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast "
-        "instead!");
+          } while (*ptr);
+
+        }
+
+        if (cont) {
+
+          cont_act = 1;
+          cont = 0;
+
+        }
+
+      }
+
+      if (count) { process_params(aflcc, scan, count, args); }
+
+      // we cannot free args[] unless we don't need
+      // to keep any reference in cc_params
+      if (scan) {
+
+        if (count) do {
+
+            free(args[--count]);
+
+          } while (count);
+
+        free(args);
+
+      }
+
+      free(tmpbuf);
+
+      continue;
+
+    }
+
+    if (!scan) insert_param(aflcc, cur);
 
   }
 
-  if (debug) {
+}
 
-    DEBUGF("cd '%s';", getthecwd());
-    for (i = 0; i < argc; i++)
-      SAYF(" '%s'", argv[i]);
-    SAYF("\n");
-    fflush(stdout);
-    fflush(stderr);
+/* Copy argv to cc_params, making the necessary edits. */
+
+static void edit_params(aflcc_state_t *aflcc, u32 argc, char **argv,
+                        char **envp) {
+
+  add_real_argv0(aflcc);
+
+  // prevent unnecessary build errors
+  if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC) {
+
+    insert_param(aflcc, "-Wno-unused-command-line-argument");
 
   }
 
-  if (getenv("AFL_LLVM_LAF_ALL")) {
+  if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) {
 
-    setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1);
-    setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1);
-    setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1);
-    setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1);
+    add_assembler(aflcc);
 
   }
 
-  cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") ||
-                getenv("AFL_GCC_CMPLOG");
+  if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); }
 
-#if !defined(__ANDROID__) && !defined(ANDROID)
-  ptr = find_object("afl-compiler-rt.o", argv[0]);
+  if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) {
 
-  if (!ptr) {
+    if (aflcc->lto_mode && aflcc->have_instr_env) {
 
-    FATAL(
-        "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH "
-        "environment variable.");
+      load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so");
 
-  }
+    }
 
-  if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); }
+    if (getenv("AFL_LLVM_DICT2FILE")) {
 
-  ck_free(ptr);
-#endif
+      load_llvm_pass(aflcc, "afl-llvm-dict2file.so");
 
-  edit_params(argc, argv, envp);
+    }
+
+    // laf
+    if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
 
-  if (debug) {
+      load_llvm_pass(aflcc, "split-switches-pass.so");
 
-    DEBUGF("cd '%s';", getthecwd());
-    for (i = 0; i < (s32)cc_par_cnt; i++)
-      SAYF(" '%s'", cc_params[i]);
-    SAYF("\n");
-    fflush(stdout);
-    fflush(stderr);
+    }
+
+    if (getenv("LAF_TRANSFORM_COMPARES") ||
+        getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
+
+      load_llvm_pass(aflcc, "compare-transform-pass.so");
+
+    }
+
+    if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") ||
+        getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) {
+
+      load_llvm_pass(aflcc, "split-compares-pass.so");
+
+    }
+
+    // /laf
+
+    if (aflcc->cmplog_mode) {
+
+      insert_param(aflcc, "-fno-inline");
+
+      load_llvm_pass(aflcc, "cmplog-switches-pass.so");
+      // reuse split switches from laf
+      load_llvm_pass(aflcc, "split-switches-pass.so");
+
+    }
+
+    // #if LLVM_MAJOR >= 13
+    //     // Use the old pass manager in LLVM 14 which the AFL++ passes still
+    //     use. insert_param(aflcc, "-flegacy-pass-manager");
+    // #endif
+
+    if (aflcc->lto_mode) {
+
+      insert_param(aflcc, aflcc->lto_flag);
+
+      if (!aflcc->have_c) {
+
+        add_lto_linker(aflcc);
+        add_lto_passes(aflcc);
+
+      }
+
+    } else {
+
+      if (aflcc->instrument_mode == INSTRUMENT_PCGUARD) {
+
+        add_optimized_pcguard(aflcc);
+
+      } else if (aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) {
+
+        add_native_pcguard(aflcc);
+
+      } else {
+
+        load_llvm_pass(aflcc, "afl-llvm-pass.so");
+
+      }
+
+    }
+
+    if (aflcc->cmplog_mode) {
+
+      load_llvm_pass(aflcc, "cmplog-instructions-pass.so");
+      load_llvm_pass(aflcc, "cmplog-routines-pass.so");
+
+    }
+
+    if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
+        getenv("AFL_LLVM_INJECTIONS_SQL") ||
+        getenv("AFL_LLVM_INJECTIONS_LDAP") ||
+        getenv("AFL_LLVM_INJECTIONS_XSS")) {
+
+      load_llvm_pass(aflcc, "injection-pass.so");
+
+    }
+
+    // insert_param(aflcc, "-Qunused-arguments");
 
   }
 
-  if (passthrough) {
+  /* Inspect the command line parameters. */
+
+  process_params(aflcc, 0, argc, argv);
+
+  add_sanitizers(aflcc, envp);
+
+  add_misc_params(aflcc);
+
+  add_defs_common(aflcc);
+  add_defs_selective_instr(aflcc);
+  add_defs_persistent_mode(aflcc);
+
+  add_runtime(aflcc);
+
+  insert_param(aflcc, NULL);
+
+}
+
+/* Main entry point */
+
+int main(int argc, char **argv, char **envp) {
+
+  aflcc_state_t *aflcc = malloc(sizeof(aflcc_state_t));
+  aflcc_state_init(aflcc, (u8 *)argv[0]);
+
+  check_environment_vars(envp);
+
+  find_built_deps(aflcc);
+
+  compiler_mode_by_callname(aflcc);
+  compiler_mode_by_environ(aflcc);
+  compiler_mode_by_cmdline(aflcc, argc, argv);
+
+  instrument_mode_by_environ(aflcc);
+
+  mode_final_checkout(aflcc, argc, argv);
+
+  process_params(aflcc, 1, argc, argv);
+
+  maybe_usage(aflcc, argc, argv);
+
+  mode_notification(aflcc);
+
+  if (aflcc->debug) debugf_args(argc, argv);
+
+  edit_params(aflcc, argc, argv, envp);
+
+  if (aflcc->debug)
+    debugf_args((s32)aflcc->cc_par_cnt, (char **)aflcc->cc_params);
+
+  if (aflcc->passthrough) {
 
-    argv[0] = cc_params[0];
-    execvp(cc_params[0], (char **)argv);
+    argv[0] = aflcc->cc_params[0];
+    execvp(aflcc->cc_params[0], (char **)argv);
 
   } else {
 
-    execvp(cc_params[0], (char **)cc_params);
+    execvp(aflcc->cc_params[0], (char **)aflcc->cc_params);
 
   }
 
-  FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
+  FATAL("Oops, failed to execute '%s' - check your PATH", aflcc->cc_params[0]);
 
   return 0;
 
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index 34a5ff81..1ee8ebe7 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -169,20 +169,16 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
 
     }
 
-    if (unlikely(afl->custom_mutators_count)) {
-
-      LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
-
-        if (el->afl_custom_fuzz_send) {
+    LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
 
-          el->afl_custom_fuzz_send(el->data, *mem, new_size);
-          sent = 1;
+      if (el->afl_custom_fuzz_send) {
 
-        }
+        el->afl_custom_fuzz_send(el->data, *mem, new_size);
+        sent = 1;
 
-      });
+      }
 
-    }
+    });
 
     if (likely(!sent)) {
 
@@ -203,7 +199,7 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
 
     }
 
-  } else {
+  } else {                                   /* !afl->custom_mutators_count */
 
     if (unlikely(len < afl->min_length && !fix)) {
 
@@ -215,27 +211,8 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
 
     }
 
-    if (unlikely(afl->custom_mutators_count)) {
-
-      LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
-
-        if (el->afl_custom_fuzz_send) {
-
-          el->afl_custom_fuzz_send(el->data, *mem, len);
-          sent = 1;
-
-        }
-
-      });
-
-    }
-
-    if (likely(!sent)) {
-
-      /* boring uncustom. */
-      afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len);
-
-    }
+    /* boring uncustom. */
+    afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len);
 
   }
 
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 17949fd7..2d5787e8 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -1812,6 +1812,10 @@ int main(int argc, char **argv_orig, char **envp) {
   check_cpu_governor(afl);
   #endif
 
+  #ifdef __APPLE__
+  setenv("DYLD_NO_PIE", "1", 0);
+  #endif
+
   if (getenv("LD_PRELOAD")) {
 
     WARNF(