about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--GNUmakefile.llvm9
-rw-r--r--coresight_mode/README.md8
-rw-r--r--docs/technical_details.md2
-rw-r--r--frida_mode/GNUmakefile2
-rw-r--r--frida_mode/Scripting.md333
-rw-r--r--frida_mode/frida.map1
-rw-r--r--frida_mode/include/asan.h1
-rw-r--r--frida_mode/include/js.h3
-rw-r--r--frida_mode/src/asan/asan.c21
-rw-r--r--frida_mode/src/asan/asan_arm64.c2
-rw-r--r--frida_mode/src/asan/asan_x64.c2
-rw-r--r--frida_mode/src/asan/asan_x86.c2
-rw-r--r--frida_mode/src/cmplog/cmplog.c18
-rw-r--r--frida_mode/src/ctx/ctx_arm32.c1
-rw-r--r--frida_mode/src/ctx/ctx_arm64.c1
-rw-r--r--frida_mode/src/ctx/ctx_x86.c1
-rw-r--r--frida_mode/src/instrument/instrument_x64.c26
-rw-r--r--frida_mode/src/instrument/instrument_x86.c202
-rw-r--r--frida_mode/src/js/api.js10
-rw-r--r--frida_mode/src/js/js.c3
-rw-r--r--frida_mode/src/js/js_api.c19
-rw-r--r--frida_mode/src/main.c20
-rw-r--r--frida_mode/src/persistent/persistent_x86.c1
-rw-r--r--frida_mode/src/ranges.c20
-rw-r--r--frida_mode/src/seccomp/seccomp_event.c6
-rw-r--r--frida_mode/src/seccomp/seccomp_filter.c6
-rw-r--r--frida_mode/src/stalker.c17
-rw-r--r--frida_mode/test/freetype2/GNUmakefile4
-rw-r--r--frida_mode/test/js/GNUmakefile28
-rw-r--r--frida_mode/test/js/fuzz.js41
-rw-r--r--frida_mode/test/js/main.js44
-rw-r--r--frida_mode/test/png/persistent/hook/GNUmakefile1
-rw-r--r--frida_mode/test/vorbis/GNUmakefile200
-rw-r--r--frida_mode/test/vorbis/Makefile13
-rwxr-xr-xfrida_mode/test/vorbis/get_symbol_addr.py36
-rw-r--r--frida_mode/ts/lib/afl.ts23
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc18
-rw-r--r--instrumentation/afl-compiler-rt.o.c156
-rw-r--r--instrumentation/afl-llvm-dict2file.so.cc21
-rw-r--r--instrumentation/afl-llvm-lto-instrumentation.so.cc1119
-rw-r--r--instrumentation/afl-llvm-pass.so.cc91
-rw-r--r--instrumentation/cmplog-instructions-pass.cc41
-rw-r--r--instrumentation/compare-transform-pass.so.cc122
-rw-r--r--instrumentation/split-compares-pass.so.cc134
-rw-r--r--instrumentation/split-switches-pass.so.cc107
-rw-r--r--src/afl-cc.c53
47 files changed, 1524 insertions, 1467 deletions
diff --git a/.gitmodules b/.gitmodules
index cd9d73e9..6569c0b1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -18,4 +18,4 @@
 	url = https://github.com/NixOS/patchelf.git
 [submodule "coresight_mode/coresight-trace"]
 	path = coresight_mode/coresight-trace
-	url = git@github.com:RICSecLab/coresight-trace.git
+	url = https://github.com/RICSecLab/coresight-trace.git
diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm
index 64e5beb2..1e2c411d 100644
--- a/GNUmakefile.llvm
+++ b/GNUmakefile.llvm
@@ -308,7 +308,7 @@ ifeq "$(TEST_MMAP)" "1"
 endif
 
 PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o 
-PROGS        = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./cmplog-switches-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so
+PROGS        = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./cmplog-switches-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./SanitizerCoverageLTO.so
 
 # If prerequisites are not given, warn, do not build anything, and exit with code 0
 ifeq "$(LLVMVER)" ""
@@ -410,11 +410,6 @@ endif
 ./SanitizerCoverageLTO.so: instrumentation/SanitizerCoverageLTO.so.cc
 ifeq "$(LLVM_LTO)" "1"
 	$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
-endif
-
-./afl-llvm-lto-instrumentation.so: instrumentation/afl-llvm-lto-instrumentation.so.cc instrumentation/afl-llvm-common.o
-ifeq "$(LLVM_LTO)" "1"
-	$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
 	$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto.o
 	@$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
 	@$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
@@ -480,7 +475,7 @@ install: all
 	@if [ -f ./afl-cc ]; then set -e; install -m 755 ./afl-cc $${DESTDIR}$(BIN_PATH); ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-c++; fi
 	@rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt*.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt*.o
 	@if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o ;fi
-	@if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-lto-instrumentation.so ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi
+	@if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi
 	@if [ -f ./afl-ld-lto ]; then set -e; install -m 755 ./afl-ld-lto $${DESTDIR}$(BIN_PATH); fi
 	@if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o ;fi
 	@if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o ; fi
diff --git a/coresight_mode/README.md b/coresight_mode/README.md
index dac44076..cd1bccab 100644
--- a/coresight_mode/README.md
+++ b/coresight_mode/README.md
@@ -1,12 +1,16 @@
 # AFL++ CoreSight mode
 
-CoreSight mode enables binary-only fuzzing on ARM64 Linux using CoreSight.
+CoreSight mode enables binary-only fuzzing on ARM64 Linux using CoreSight (ARM's hardware tracing technology).
 
 NOTE: CoreSight mode is in the early development stage. Not applicable for production use.
+Currently the following hardware boards are supported: 
+* NVIDIA Jetson TX2 (NVIDIA Parker)
+* NVIDIA Jetson Nano (NVIDIA Tegra X1)
+* GIGABYTE R181-T90 (Marvell ThunderX2 CN99XX)
 
 ## Getting started
 
-Please read the [RICSec/coresight-trace README](https://github.com/RICSecLab/coresight-trace/blob/master/README.md) and check the prerequisites before getting started.
+Please read the [RICSec/coresight-trace README](https://github.com/RICSecLab/coresight-trace/blob/master/README.md) and check the prerequisites (capstone) before getting started.
 
 CoreSight mode supports the AFL fork server mode to reduce `exec` system call overhead. To support it for binary-only fuzzing, it needs to modify the target ELF binary to re-link to the patched glibc. We employ this design from [PTrix](https://github.com/junxzm1990/afl-pt).
 
diff --git a/docs/technical_details.md b/docs/technical_details.md
index b9d271d9..994ffe9f 100644
--- a/docs/technical_details.md
+++ b/docs/technical_details.md
@@ -1,7 +1,7 @@
 # Technical "whitepaper" for afl-fuzz
 
 
-NOTE: this document is rather outdated!
+NOTE: this document is mostly outdated!
 
 
 This document provides a quick overview of the guts of American Fuzzy Lop.
diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile
index c0abe14c..52439979 100644
--- a/frida_mode/GNUmakefile
+++ b/frida_mode/GNUmakefile
@@ -103,7 +103,7 @@ ifndef OS
  $(error "Operating system unsupported")
 endif
 
-GUM_DEVKIT_VERSION=15.1.10
+GUM_DEVKIT_VERSION=15.1.11
 GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz
 GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)"
 
diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md
index 691b03d1..2ee0c858 100644
--- a/frida_mode/Scripting.md
+++ b/frida_mode/Scripting.md
@@ -246,7 +246,7 @@ FRIDA mode supports the replacement of any function, with an implementation
 generated by CModule. This allows for a bespoke harness to be written as
 follows:
 
-```
+```js
 const slow = DebugSymbol.fromName('slow').address;
 Afl.print(`slow: ${slow}`);
 
@@ -281,13 +281,90 @@ Afl.done();
 Here, we replace the function `slow` with our own code. This code is then
 selected as the entry point as well as the persistent loop address.
 
-**WARNING** There are two key limitations in replacing a function in this way:
-- The function which is to be replaced must not be `main` this is because this
-is the point at which FRIDA mode is initialized and at the point the the JS has
-been run, the start of the `main` function has already been instrumented and
-cached.
-- The replacement function must not call itself. e.g. in this example we
-couldn't replace `LLVMFuzzerTestOneInput` and call itself.
+## Replacing LLVMFuzzerTestOneInput
+The function `LLVMFuzzerTestOneInput` can be replaced just like any other. Also
+any replaced function can also call itself. In the example below, we replace
+`LLVMFuzzerTestOneInput` with `My_LLVMFuzzerTestOneInput` which ignores the
+parameters `buf` and `len` and then calls the original `LLVMFuzzerTestOneInput`
+with the paramaters `__afl_fuzz_ptr` and `__afl_fuzz_len`. This allows us to
+carry out in-memory fuzzing without the need for any hook function. It should be
+noted that the replacement function and the original can *NOT* share the same
+name, since otherwise the `C` code in the `CModule` will not compile due to a
+symbol name collision.
+
+```js
+const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
+Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);
+
+const cm = new CModule(`
+
+    extern unsigned char * __afl_fuzz_ptr;
+    extern unsigned int * __afl_fuzz_len;
+    extern void LLVMFuzzerTestOneInput(char *buf, int len);
+
+    void My_LLVMFuzzerTestOneInput(char *buf, int len) {
+
+      LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
+
+    }
+    `,
+    {
+        LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
+        __afl_fuzz_ptr: Afl.getAflFuzzPtr(),
+        __afl_fuzz_len: Afl.getAflFuzzLen()
+    });
+
+Afl.setEntryPoint(cm.My_LLVMFuzzerTestOneInput);
+Afl.setPersistentAddress(cm.My_LLVMFuzzerTestOneInput);
+Afl.setInMemoryFuzzing();
+Interceptor.replace(LLVMFuzzerTestOneInput, cm.My_LLVMFuzzerTestOneInput);
+```
+
+## Hooking `main`
+Lastly, it should be noted that using FRIDA mode's scripting support to hook
+the `main` function is a special case. This is because the `main` function is
+already hooked by the FRIDA mode engine itself and hence the function `main` (or
+at least the first basic block already been compiled by Stalker ready for
+execution). Hence any attempt to use `Interceptor.replace` like in the example
+above will not work. Instead the JS bindings provide a function `setJsMainHook`
+for just this scenario as demonstrated in the example below.
+
+```js
+const main = DebugSymbol.fromName('main').address;
+Afl.print(`main: ${main}`);
+
+const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
+Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);
+
+const cm = new CModule(`
+
+    extern unsigned char * __afl_fuzz_ptr;
+    extern unsigned int * __afl_fuzz_len;
+    extern void LLVMFuzzerTestOneInput(char *buf, int len);
+
+    int main(int argc, char **argv)  {
+
+      LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
+
+    }
+    `,
+    {
+        LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
+        __afl_fuzz_ptr: Afl.getAflFuzzPtr(),
+        __afl_fuzz_len: Afl.getAflFuzzLen()
+    });
+
+Afl.setEntryPoint(cm.main);
+Afl.setPersistentAddress(cm.main);
+Afl.setInMemoryFuzzing();
+Afl.setJsMainHook(cm.main);
+```
+## Library Fuzzing
+
+It doesn't take too much imagination to see that the above example can be
+extended to use FRIDA's `Module.load` API so that the replaced `main` function
+can then call an arbitrary function. In this way, if we have a library which we
+wish to fuzz rather than an execuatble, then a surrogate executable can be used.
 
 # Patching
 Consider the [following](test/js/test2.c) test code...
@@ -620,41 +697,31 @@ value of the `-t` flag passed to `afl-fuzz`.
 # API
 ```js
 class Afl {
-
-  /**
-   * Field containing the `Module` object for `afl-frida-trace.so` (the FRIDA mode
-   * implementation).
-   */
-  public static module: Module = Process.getModuleByName("afl-frida-trace.so");
-
   /**
    * This is equivalent to setting a value in `AFL_FRIDA_EXCLUDE_RANGES`,
    * it takes as arguments a `NativePointer` and a `number`. It can be
    * called multiple times to exclude several ranges.
    */
-  public static addExcludedRange(addressess: NativePointer, size: number): void {
-    Afl.jsApiAddExcludeRange(addressess, size);
+  static addExcludedRange(addressess, size) {
+      Afl.jsApiAddExcludeRange(addressess, size);
   }
-
   /**
    * This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`,
    * it takes as arguments a `NativePointer` and a `number`. It can be
    * called multiple times to include several ranges.
    */
-  public static addIncludedRange(addressess: NativePointer, size: number): void {
-    Afl.jsApiAddIncludeRange(addressess, size);
+  static addIncludedRange(addressess, size) {
+      Afl.jsApiAddIncludeRange(addressess, size);
   }
-
   /**
    * This must always be called at the end of your script. This lets
    * FRIDA mode know that your configuration is finished and that
    * execution has reached the end of your script. Failure to call
    * this will result in a fatal error.
    */
-  public static done(): void {
-    Afl.jsApiDone();
+  static done() {
+      Afl.jsApiDone();
   }
-
   /**
    * This function can be called within your script to cause FRIDA
    * mode to trigger a fatal error. This is useful if for example you
@@ -662,49 +729,48 @@ class Afl {
    * stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view
    * this error message.
    */
-  public static error(msg: string): void {
-    const buf = Memory.allocUtf8String(msg);
-    Afl.jsApiError(buf);
+  static error(msg) {
+      const buf = Memory.allocUtf8String(msg);
+      Afl.jsApiError(buf);
   }
-
   /**
    * Function used to provide access to `__afl_fuzz_ptr`, which contains the length of
    * fuzzing data when using in-memory test case fuzzing.
    */
-  public static getAflFuzzLen(): NativePointer {
-
-    return Afl.jsApiGetSymbol("__afl_fuzz_len");
+  static getAflFuzzLen() {
+      return Afl.jsApiGetSymbol("__afl_fuzz_len");
   }
-
   /**
    * Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing
    * data when using in-memory test case fuzzing.
    */
-  public static getAflFuzzPtr(): NativePointer {
-
-    return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
+  static getAflFuzzPtr() {
+      return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
   }
-
   /**
    * Print a message to the STDOUT. This should be preferred to
    * FRIDA's `console.log` since FRIDA will queue it's log messages.
    * If `console.log` is used in a callback in particular, then there
    * may no longer be a thread running to service this queue.
    */
-  public static print(msg: string): void {
-    const STDOUT_FILENO = 2;
-    const log = `${msg}\n`;
-    const buf = Memory.allocUtf8String(log);
-    Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
+  static print(msg) {
+      const STDOUT_FILENO = 2;
+      const log = `${msg}\n`;
+      const buf = Memory.allocUtf8String(log);
+      Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
+  }
+  /**
+   * See `AFL_FRIDA_INST_NO_BACKPATCH`.
+   */
+  static setBackpatchDisable() {
+      Afl.jsApiSetBackpatchDisable();
   }
-
   /**
    * See `AFL_FRIDA_DEBUG_MAPS`.
    */
-  public static setDebugMaps(): void {
-    Afl.jsApiSetDebugMaps();
+  static setDebugMaps() {
+      Afl.jsApiSetDebugMaps();
   }
-
   /**
    * This has the same effect as setting `AFL_ENTRYPOINT`, but has the
    * convenience of allowing you to use FRIDAs APIs to determine the
@@ -713,143 +779,198 @@ class Afl {
    * function should be called with a `NativePointer` as its
    * argument.
    */
-  public static setEntryPoint(address: NativePointer): void {
-    Afl.jsApiSetEntryPoint(address);
+  static setEntryPoint(address) {
+      Afl.jsApiSetEntryPoint(address);
   }
-
   /**
    * Function used to enable in-memory test cases for fuzzing.
    */
-  public static setInMemoryFuzzing(): void {
-    Afl.jsApiAflSharedMemFuzzing.writeInt(1);
+  static setInMemoryFuzzing() {
+      Afl.jsApiAflSharedMemFuzzing.writeInt(1);
+  }
+  /**
+   * See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string`
+   * as an argument.
+   */
+  static setInstrumentCoverageFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetInstrumentCoverageFile(buf);
   }
-
   /**
    * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
    * an argument.
    */
-  public static setInstrumentDebugFile(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetInstrumentDebugFile(buf);
+  static setInstrumentDebugFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetInstrumentDebugFile(buf);
   }
-
   /**
    * See `AFL_FRIDA_INST_TRACE`.
    */
-  public static setInstrumentEnableTracing(): void {
-    Afl.jsApiSetInstrumentTrace();
+  static setInstrumentEnableTracing() {
+      Afl.jsApiSetInstrumentTrace();
+  }
+  /**
+   * See `AFL_FRIDA_INST_JIT`.
+   */
+  static setInstrumentJit() {
+      Afl.jsApiSetInstrumentJit();
   }
-
   /**
    * See `AFL_INST_LIBS`.
    */
-  public static setInstrumentLibraries(): void {
-    Afl.jsApiSetInstrumentLibraries();
+  static setInstrumentLibraries() {
+      Afl.jsApiSetInstrumentLibraries();
   }
-
   /**
    * See `AFL_FRIDA_INST_NO_OPTIMIZE`
    */
-  public static setInstrumentNoOptimize(): void {
-    Afl.jsApiSetInstrumentNoOptimize();
+  static setInstrumentNoOptimize() {
+      Afl.jsApiSetInstrumentNoOptimize();
+  }
+  /*
+    * See `AFL_FRIDA_INST_SEED`
+    */
+  static setInstrumentSeed(seed) {
+      Afl.jsApiSetInstrumentSeed(seed);
   }
-
   /**
    * See `AFL_FRIDA_INST_TRACE_UNIQUE`.
    */
-  public static setInstrumentTracingUnique(): void {
-    Afl.jsApiSetInstrumentTraceUnique();
+  static setInstrumentTracingUnique() {
+      Afl.jsApiSetInstrumentTraceUnique();
+  }
+  /**
+   * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single
+   * `string` as an argument.
+   */
+  static setInstrumentUnstableCoverageFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
+  }
+  /*
+    * Set a callback to be called in place of the usual `main` function. This see
+    * `Scripting.md` for details.
+    */
+  static setJsMainHook(address) {
+      Afl.jsApiSetJsMainHook(address);
   }
-
   /**
    * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
    * `NativePointer` should be provided as it's argument.
    */
-  public static setPersistentAddress(address: NativePointer): void {
-    Afl.jsApiSetPersistentAddress(address);
+  static setPersistentAddress(address) {
+      Afl.jsApiSetPersistentAddress(address);
   }
-
   /**
    * This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
    * `number` should be provided as it's argument.
    */
-  public static setPersistentCount(count: number): void {
-    Afl.jsApiSetPersistentCount(count);
+  static setPersistentCount(count) {
+      Afl.jsApiSetPersistentCount(count);
   }
-
   /**
    * See `AFL_FRIDA_PERSISTENT_DEBUG`.
    */
-  public static setPersistentDebug(): void {
-    Afl.jsApiSetPersistentDebug();
+  static setPersistentDebug() {
+      Afl.jsApiSetPersistentDebug();
   }
-
   /**
    * See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an
    * argument. See above for examples of use.
    */
-  public static setPersistentHook(address: NativePointer): void {
-    Afl.jsApiSetPersistentHook(address);
+  static setPersistentHook(address) {
+      Afl.jsApiSetPersistentHook(address);
   }
-
   /**
    * This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
    * `NativePointer` should be provided as it's argument.
    */
-  public static setPersistentReturn(address: NativePointer): void {
-    Afl.jsApiSetPersistentReturn(address);
+  static setPersistentReturn(address) {
+      Afl.jsApiSetPersistentReturn(address);
+  }
+  /**
+   * See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`.
+   */
+  static setPrefetchBackpatchDisable() {
+      Afl.jsApiSetPrefetchBackpatchDisable();
   }
-
   /**
    * See `AFL_FRIDA_INST_NO_PREFETCH`.
    */
-  public static setPrefetchDisable(): void {
-    Afl.jsApiSetPrefetchDisable();
+  static setPrefetchDisable() {
+      Afl.jsApiSetPrefetchDisable();
+  }
+  /**
+   * See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as
+   * an argument.
+   */
+  static setSeccompFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetSeccompFile(buf);
+  }
+  /**
+   * See `AFL_FRIDA_STALKER_ADJACENT_BLOCKS`.
+   */
+  static setStalkerAdjacentBlocks(val) {
+      Afl.jsApiSetStalkerAdjacentBlocks(val);
   }
-
   /*
-   * Set a function to be called for each instruction which is instrumented
-   * by AFL FRIDA mode.
+    * Set a function to be called for each instruction which is instrumented
+    * by AFL FRIDA mode.
+    */
+  static setStalkerCallback(callback) {
+      Afl.jsApiSetStalkerCallback(callback);
+  }
+  /**
+   * See `AFL_FRIDA_STALKER_IC_ENTRIES`.
    */
-  public static setStalkerCallback(callback: NativePointer): void {
-    Afl.jsApiSetStalkerCallback(callback);
+  static setStalkerIcEntries(val) {
+      Afl.jsApiSetStalkerIcEntries(val);
   }
-
   /**
    * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
    * an argument.
    */
-  public static setStatsFile(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetStatsFile(buf);
+  static setStatsFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetStatsFile(buf);
   }
-
   /**
    * See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
    * argument
    */
-  public static setStatsInterval(interval: number): void {
-    Afl.jsApiSetStatsInterval(interval);
+  static setStatsInterval(interval) {
+      Afl.jsApiSetStatsInterval(interval);
   }
-
   /**
    * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
    * an argument.
    */
-  public static setStdErr(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetStdErr(buf);
+  static setStdErr(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetStdErr(buf);
   }
-
   /**
    * See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
    * an argument.
    */
-  public static setStdOut(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetStdOut(buf);
+  static setStdOut(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetStdOut(buf);
+  }
+  /**
+   * See `AFL_FRIDA_TRACEABLE`.
+   */
+  static setTraceable() {
+      Afl.jsApiSetTraceable();
+  }
+  static jsApiGetFunction(name, retType, argTypes) {
+      const addr = Afl.module.getExportByName(name);
+      return new NativeFunction(addr, retType, argTypes);
+  }
+  static jsApiGetSymbol(name) {
+      return Afl.module.getExportByName(name);
   }
-
 }
-
 ```
diff --git a/frida_mode/frida.map b/frida_mode/frida.map
index 61eb19ee..5276db91 100644
--- a/frida_mode/frida.map
+++ b/frida_mode/frida.map
@@ -20,6 +20,7 @@
     js_api_set_instrument_trace;
     js_api_set_instrument_trace_unique;
     js_api_set_instrument_unstable_coverage_file;
+    js_api_set_js_main_hook;
     js_api_set_persistent_address;
     js_api_set_persistent_count;
     js_api_set_persistent_debug;
diff --git a/frida_mode/include/asan.h b/frida_mode/include/asan.h
index 67d33591..6745eb02 100644
--- a/frida_mode/include/asan.h
+++ b/frida_mode/include/asan.h
@@ -9,6 +9,7 @@ void asan_config(void);
 void asan_init(void);
 void asan_arch_init(void);
 void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator);
+void asan_exclude_module_by_symbol(gchar *symbol_name);
 
 #endif
 
diff --git a/frida_mode/include/js.h b/frida_mode/include/js.h
index a5ecb712..39aa0573 100644
--- a/frida_mode/include/js.h
+++ b/frida_mode/include/js.h
@@ -7,11 +7,14 @@ typedef gboolean (*js_api_stalker_callback_t)(const cs_insn *insn,
                                               gboolean begin, gboolean excluded,
                                               GumStalkerOutput *output);
 
+typedef int (*js_main_hook_t)(int argc, char **argv, char **envp);
+
 extern unsigned char api_js[];
 extern unsigned int  api_js_len;
 
 extern gboolean                  js_done;
 extern js_api_stalker_callback_t js_user_callback;
+extern js_main_hook_t            js_main_hook;
 
 /* Frida Mode */
 
diff --git a/frida_mode/src/asan/asan.c b/frida_mode/src/asan/asan.c
index d649bd76..884bec53 100644
--- a/frida_mode/src/asan/asan.c
+++ b/frida_mode/src/asan/asan.c
@@ -1,6 +1,7 @@
 #include "frida-gumjs.h"
 
 #include "asan.h"
+#include "ranges.h"
 #include "util.h"
 
 static gboolean asan_enabled = FALSE;
@@ -32,3 +33,23 @@ void asan_init(void) {
 
 }
 
+static gboolean asan_exclude_module(const GumModuleDetails *details,
+                                    gpointer                user_data) {
+
+  gchar *    symbol_name = (gchar *)user_data;
+  GumAddress address;
+
+  address = gum_module_find_export_by_name(details->name, symbol_name);
+  if (address == 0) { return TRUE; }
+
+  ranges_add_exclude((GumMemoryRange *)details->range);
+  return FALSE;
+
+}
+
+void asan_exclude_module_by_symbol(gchar *symbol_name) {
+
+  gum_process_enumerate_modules(asan_exclude_module, symbol_name);
+
+}
+
diff --git a/frida_mode/src/asan/asan_arm64.c b/frida_mode/src/asan/asan_arm64.c
index 88c76535..b2adfa52 100644
--- a/frida_mode/src/asan/asan_arm64.c
+++ b/frida_mode/src/asan/asan_arm64.c
@@ -88,6 +88,8 @@ void asan_arch_init(void) {
 
   }
 
+  asan_exclude_module_by_symbol("__asan_loadN");
+
 }
 
 #endif
diff --git a/frida_mode/src/asan/asan_x64.c b/frida_mode/src/asan/asan_x64.c
index c7b70967..a287ea34 100644
--- a/frida_mode/src/asan/asan_x64.c
+++ b/frida_mode/src/asan/asan_x64.c
@@ -85,6 +85,8 @@ void asan_arch_init(void) {
 
   }
 
+  asan_exclude_module_by_symbol("__asan_loadN");
+
 }
 
 #endif
diff --git a/frida_mode/src/asan/asan_x86.c b/frida_mode/src/asan/asan_x86.c
index afc89936..331d026b 100644
--- a/frida_mode/src/asan/asan_x86.c
+++ b/frida_mode/src/asan/asan_x86.c
@@ -85,6 +85,8 @@ void asan_arch_init(void) {
 
   }
 
+  asan_exclude_module_by_symbol("__asan_loadN");
+
 }
 
 #endif
diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c
index 81e1a4b0..443baa1d 100644
--- a/frida_mode/src/cmplog/cmplog.c
+++ b/frida_mode/src/cmplog/cmplog.c
@@ -33,8 +33,22 @@ static gboolean cmplog_range(const GumRangeDetails *details,
 
 static gint cmplog_sort(gconstpointer a, gconstpointer b) {
 
-  return ((GumMemoryRange *)b)->base_address -
-         ((GumMemoryRange *)a)->base_address;
+  GumMemoryRange *ra = (GumMemoryRange *)a;
+  GumMemoryRange *rb = (GumMemoryRange *)b;
+
+  if (ra->base_address < rb->base_address) {
+
+    return -1;
+
+  } else if (ra->base_address > rb->base_address) {
+
+    return 1;
+
+  } else {
+
+    return 0;
+
+  }
 
 }
 
diff --git a/frida_mode/src/ctx/ctx_arm32.c b/frida_mode/src/ctx/ctx_arm32.c
index 049b5548..28fc706b 100644
--- a/frida_mode/src/ctx/ctx_arm32.c
+++ b/frida_mode/src/ctx/ctx_arm32.c
@@ -1,6 +1,7 @@
 #include "frida-gumjs.h"
 
 #include "ctx.h"
+#include "util.h"
 
 #if defined(__arm__)
 
diff --git a/frida_mode/src/ctx/ctx_arm64.c b/frida_mode/src/ctx/ctx_arm64.c
index 01f321e3..63b6cf09 100644
--- a/frida_mode/src/ctx/ctx_arm64.c
+++ b/frida_mode/src/ctx/ctx_arm64.c
@@ -1,6 +1,7 @@
 #include "frida-gumjs.h"
 
 #include "ctx.h"
+#include "util.h"
 
 #if defined(__aarch64__)
 
diff --git a/frida_mode/src/ctx/ctx_x86.c b/frida_mode/src/ctx/ctx_x86.c
index abfeafc8..438e1fde 100644
--- a/frida_mode/src/ctx/ctx_x86.c
+++ b/frida_mode/src/ctx/ctx_x86.c
@@ -1,6 +1,7 @@
 #include "frida-gumjs.h"
 
 #include "ctx.h"
+#include "util.h"
 
 #if defined(__i386__)
 
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index c271adc1..41162f2a 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -5,7 +5,7 @@
 
 #if defined(__linux__)
   #if !defined(__ANDROID__)
-    #include <asm/prctl.h>
+    #include <sys/prctl.h>
     #include <sys/syscall.h>
   #else
     #include <linux/ashmem.h>
@@ -68,10 +68,10 @@ typedef struct {
   //  sahf
   //  mov    rax,QWORD PTR [rsp-0x80]
 
-  uint8_t mov_rax_rsp_80[5];
-  uint8_t lahf;
   uint8_t mov_rax_rsp_88[8];
-  uint8_t mov_rbx_rsp_90[8];
+  uint8_t lahf;
+  uint8_t mov_rax_rsp_90[8];
+  uint8_t mov_rbx_rsp_98[8];
 
   uint8_t mov_eax_prev_loc[6];
   uint8_t mov_prev_loc_curr_loc_shr1[10];
@@ -83,10 +83,10 @@ typedef struct {
   uint8_t adc_bl_0[3];
   uint8_t mov_ptr_rax_rbx[2];
 
-  uint8_t mov_rsp_90_rbx[8];
-  uint8_t mov_rsp_88_rax[8];
+  uint8_t mov_rsp_98_rbx[8];
+  uint8_t mov_rsp_90_rax[8];
   uint8_t sahf;
-  uint8_t mov_rsp_80_rax[5];
+  uint8_t mov_rsp_88_rax[8];
 
 } afl_log_code_asm_t;
 
@@ -102,10 +102,10 @@ typedef union {
 static const afl_log_code_asm_t template =
     {
 
-        .mov_rax_rsp_80 = {0x48, 0x89, 0x44, 0x24, 0x80},
-        .lahf = 0x9f,
         .mov_rax_rsp_88 = {0x48, 0x89, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
-        .mov_rbx_rsp_90 = {0x48, 0x89, 0x9C, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
+        .lahf = 0x9f,
+        .mov_rax_rsp_90 = {0x48, 0x89, 0x84, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
+        .mov_rbx_rsp_98 = {0x48, 0x89, 0x9C, 0x24, 0x68, 0xFF, 0xFF, 0xFF},
 
         .mov_eax_prev_loc = {0x8b, 0x05},
         .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05},
@@ -116,10 +116,10 @@ static const afl_log_code_asm_t template =
         .adc_bl_0 = {0x80, 0xd3, 0x00},
         .mov_ptr_rax_rbx = {0x88, 0x18},
 
-        .mov_rsp_90_rbx = {0x48, 0x8B, 0x9C, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
-        .mov_rsp_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
+        .mov_rsp_98_rbx = {0x48, 0x8B, 0x9C, 0x24, 0x68, 0xFF, 0xFF, 0xFF},
+        .mov_rsp_90_rax = {0x48, 0x8B, 0x84, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
         .sahf = 0x9e,
-        .mov_rsp_80_rax = {0x48, 0x8B, 0x44, 0x24, 0x80},
+        .mov_rsp_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
 
 }
 
diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c
index 79664afa..ad837e2d 100644
--- a/frida_mode/src/instrument/instrument_x86.c
+++ b/frida_mode/src/instrument/instrument_x86.c
@@ -1,67 +1,144 @@
 #include "frida-gumjs.h"
 
 #include "instrument.h"
+#include "stalker.h"
 #include "util.h"
 
 #if defined(__i386__)
 
-static GumAddress current_log_impl = GUM_ADDRESS(0);
+static GHashTable *coverage_blocks = NULL;
 
-static void instrument_coverage_function(GumX86Writer *cw) {
+  #pragma pack(push, 1)
+typedef struct {
 
-  gum_x86_writer_put_pushfx(cw);
-  gum_x86_writer_put_push_reg(cw, GUM_REG_ECX);
-  gum_x86_writer_put_push_reg(cw, GUM_REG_EDX);
+  // cur_location = (block_address >> 4) ^ (block_address << 8);
+  // shared_mem[cur_location ^ prev_location]++;
+  // prev_location = cur_location >> 1;
 
-  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
-                                     GUM_ADDRESS(&instrument_previous_pc));
-  gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_EDX, GUM_REG_ECX);
-  gum_x86_writer_put_xor_reg_reg(cw, GUM_REG_EDX, GUM_REG_EDI);
+  uint8_t mov_eax_esp_4[4];
+  uint8_t lahf;
+  uint8_t mov_eax_esp_8[4];
+  uint8_t mov_ebx_esp_c[4];
 
-  gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EDX, GUM_ADDRESS(__afl_area_ptr));
+  uint8_t mov_eax_prev_loc[5];
+  uint8_t mov_prev_loc_curr_loc_shr1[10];
 
-  /* add byte ptr [edx], 1 */
-  uint8_t add_byte_ptr_edx_1[] = {0x80, 0x02, 0x01};
-  gum_x86_writer_put_bytes(cw, add_byte_ptr_edx_1, sizeof(add_byte_ptr_edx_1));
+  uint8_t xor_eax_curr_loc[5];
+  uint8_t add_eax_area_ptr[5];
 
-  /* adc byte ptr [edx], 0 */
-  uint8_t adc_byte_ptr_edx_0[] = {0x80, 0x12, 0x00};
-  gum_x86_writer_put_bytes(cw, adc_byte_ptr_edx_0, sizeof(adc_byte_ptr_edx_0));
+  uint8_t mov_ebx_ptr_eax[2];
+  uint8_t add_bl_1[3];
+  uint8_t adc_bl_0[3];
+  uint8_t mov_ptr_eax_ebx[2];
 
-  uint8_t ror_di_1[] = {0x66, 0xd1, 0xcf};
-  gum_x86_writer_put_bytes(cw, ror_di_1, sizeof(ror_di_1));
-  gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_ECX, GUM_REG_EDI);
+  uint8_t mov_esp_c_ebx[4];
+  uint8_t mov_esp_8_eax[4];
+  uint8_t sahf;
+  uint8_t mov_esp_4_eax[4];
 
-  gum_x86_writer_put_pop_reg(cw, GUM_REG_EDX);
-  gum_x86_writer_put_pop_reg(cw, GUM_REG_ECX);
-  gum_x86_writer_put_popfx(cw);
-  gum_x86_writer_put_ret(cw);
+} afl_log_code_asm_t;
+
+  #pragma pack(pop)
+
+typedef union {
+
+  afl_log_code_asm_t code;
+  uint8_t            bytes[0];
+
+} afl_log_code;
+
+static const afl_log_code_asm_t template =
+    {
+
+        .mov_eax_esp_4 = {0x89, 0x44, 0x24, 0xFC},
+        .lahf = 0x9f,
+        .mov_eax_esp_8 = {0x89, 0x44, 0x24, 0xF8},
+        .mov_ebx_esp_c = {0x89, 0x5C, 0x24, 0xF4},
+
+        .mov_eax_prev_loc = {0xA1},
+        .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05},
+
+        .xor_eax_curr_loc = {0x35},
+        .add_eax_area_ptr = {0x05},
+        .mov_ebx_ptr_eax = {0x8a, 0x18},
+        .add_bl_1 = {0x80, 0xc3, 0x01},
+        .adc_bl_0 = {0x80, 0xd3, 0x00},
+        .mov_ptr_eax_ebx = {0x88, 0x18},
+
+        .mov_esp_c_ebx = {0x8B, 0x5C, 0x24, 0xF4},
+        .mov_esp_8_eax = {0x8B, 0x44, 0x24, 0xF8},
+        .sahf = 0x9e,
+        .mov_esp_4_eax = {0x8B, 0x44, 0x24, 0xFC},
 
 }
 
+;
+
 gboolean instrument_is_coverage_optimize_supported(void) {
 
   return true;
 
 }
 
-static void instrument_coverate_write_function(GumStalkerOutput *output) {
+static void instrument_coverage_switch(GumStalkerObserver *self,
+                                       gpointer            start_address,
+                                       const cs_insn *     from_insn,
+                                       gpointer *          target) {
 
-  GumX86Writer *cw = output->writer.x86;
+  UNUSED_PARAMETER(self);
+  UNUSED_PARAMETER(start_address);
+
+  cs_x86 *   x86;
+  cs_x86_op *op;
+  if (from_insn == NULL) { return; }
+
+  x86 = &from_insn->detail->x86;
+  op = x86->operands;
+
+  if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target))) {
+
+    return;
+
+  }
+
+  switch (from_insn->id) {
+
+    case X86_INS_CALL:
+    case X86_INS_JMP:
+      if (x86->op_count != 1) {
+
+        FATAL("Unexpected operand count: %d", x86->op_count);
+
+      }
+
+      if (op[0].type != X86_OP_IMM) { return; }
+
+      break;
+    case X86_INS_RET:
+      break;
+    default:
+      return;
 
-  if (current_log_impl == 0 ||
-      !gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) ||
-      !gum_x86_writer_can_branch_directly_between(cw->pc + 128,
-                                                  current_log_impl)) {
+  }
+
+  *target = (guint8 *)*target + sizeof(afl_log_code);
+
+}
+
+static void instrument_coverage_suppress_init(void) {
 
-    gconstpointer after_log_impl = cw->code + 1;
+  static gboolean initialized = false;
+  if (initialized) { return; }
+  initialized = true;
 
-    gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
+  GumStalkerObserver *         observer = stalker_get_observer();
+  GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer);
+  iface->switch_callback = instrument_coverage_switch;
 
-    current_log_impl = cw->pc;
-    instrument_coverage_function(cw);
+  coverage_blocks = g_hash_table_new(g_direct_hash, g_direct_equal);
+  if (coverage_blocks == NULL) {
 
-    gum_x86_writer_put_label(cw, after_log_impl);
+    FATAL("Failed to g_hash_table_new, errno: %d", errno);
 
   }
 
@@ -70,14 +147,61 @@ static void instrument_coverate_write_function(GumStalkerOutput *output) {
 void instrument_coverage_optimize(const cs_insn *   instr,
                                   GumStalkerOutput *output) {
 
+  afl_log_code  code = {0};
   GumX86Writer *cw = output->writer.x86;
   guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
-  instrument_coverate_write_function(output);
+  gsize   map_size_pow2;
+  gsize   area_offset_ror;
+
+  code.code = template;
+
+  instrument_coverage_suppress_init();
+
+  // gum_x86_writer_put_breakpoint(cw);
+
+  if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
+
+    FATAL("Failed - g_hash_table_add");
+
+  }
+
+  gssize prev_loc_value_offset2 =
+      offsetof(afl_log_code, code.mov_eax_prev_loc) +
+      sizeof(code.code.mov_eax_prev_loc) - sizeof(gint);
+
+  *((gint *)&code.bytes[prev_loc_value_offset2]) =
+      (gint)GPOINTER_TO_SIZE(&instrument_previous_pc);
+
+  gssize curr_loc_shr_1_offset =
+      offsetof(afl_log_code, code.mov_prev_loc_curr_loc_shr1) +
+      sizeof(code.code.mov_prev_loc_curr_loc_shr1) - sizeof(guint32);
+
+  map_size_pow2 = util_log2(__afl_map_size);
+  area_offset_ror = util_rotate(area_offset, 1, map_size_pow2);
+
+  *((guint32 *)&code.bytes[curr_loc_shr_1_offset]) = (guint32)(area_offset_ror);
+
+  gssize prev_loc_value_offset =
+      offsetof(afl_log_code, code.mov_prev_loc_curr_loc_shr1) +
+      sizeof(code.code.mov_prev_loc_curr_loc_shr1) - sizeof(gint) -
+      sizeof(guint32);
+
+  *((gint *)&code.bytes[prev_loc_value_offset]) =
+      (gint)GPOINTER_TO_SIZE(&instrument_previous_pc);
+
+  gssize xor_curr_loc_offset = offsetof(afl_log_code, code.xor_eax_curr_loc) +
+                               sizeof(code.code.xor_eax_curr_loc) -
+                               sizeof(guint32);
+
+  *((guint32 *)&code.bytes[xor_curr_loc_offset]) = (guint32)area_offset;
+
+  gssize add_area_ptr_offset = offsetof(afl_log_code, code.add_eax_area_ptr) +
+                               sizeof(code.code.add_eax_area_ptr) -
+                               sizeof(guint32);
+
+  *((guint32 *)&code.bytes[add_area_ptr_offset]) = (guint32)__afl_area_ptr;
 
-  gum_x86_writer_put_push_reg(cw, GUM_REG_EDI);
-  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDI, area_offset);
-  gum_x86_writer_put_call_address(cw, current_log_impl);
-  gum_x86_writer_put_pop_reg(cw, GUM_REG_EDI);
+  gum_x86_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
 
 }
 
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
index 8e810d09..215fbdaf 100644
--- a/frida_mode/src/js/api.js
+++ b/frida_mode/src/js/api.js
@@ -151,6 +151,13 @@ class Afl {
         const buf = Memory.allocUtf8String(file);
         Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
     }
+    /*
+     * Set a callback to be called in place of the usual `main` function. This see
+     * `Scripting.md` for details.
+     */
+    static setJsMainHook(address) {
+        Afl.jsApiSetJsMainHook(address);
+    }
     /**
      * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
      * `NativePointer` should be provided as it's argument.
@@ -291,6 +298,7 @@ Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed",
 Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
 Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []);
 Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]);
+Afl.jsApiSetJsMainHook = Afl.jsApiGetFunction("js_api_set_js_main_hook", "void", ["pointer"]);
 Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]);
 Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]);
 Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []);
@@ -299,8 +307,8 @@ Afl.jsApiSetPersistentReturn = Afl.jsApiGetFunction("js_api_set_persistent_retur
 Afl.jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_backpatch_disable", "void", []);
 Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []);
 Afl.jsApiSetSeccompFile = Afl.jsApiGetFunction("js_api_set_seccomp_file", "void", ["pointer"]);
-Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
 Afl.jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction("js_api_set_stalker_adjacent_blocks", "void", ["uint32"]);
+Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
 Afl.jsApiSetStalkerIcEntries = Afl.jsApiGetFunction("js_api_set_stalker_ic_entries", "void", ["uint32"]);
 Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "void", ["pointer"]);
 Afl.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]);
diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c
index 37cd377b..5f477388 100644
--- a/frida_mode/src/js/js.c
+++ b/frida_mode/src/js/js.c
@@ -3,10 +3,11 @@
 #include "js.h"
 #include "util.h"
 
-static char *             js_script = NULL;
 gboolean                  js_done = FALSE;
 js_api_stalker_callback_t js_user_callback = NULL;
+js_main_hook_t            js_main_hook = NULL;
 
+static char *              js_script = NULL;
 static gchar *             filename = "afl.js";
 static gchar *             contents;
 static GumScriptBackend *  backend;
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 102423d9..5021b531 100644
--- a/frida_mode/src/js/js_api.c
+++ b/frida_mode/src/js/js_api.c
@@ -11,6 +11,10 @@
 #include "stats.h"
 #include "util.h"
 
+typedef uint8_t u8;
+
+extern void __afl_set_persistent_mode(u8 mode);
+
 __attribute__((visibility("default"))) void js_api_done() {
 
   js_done = TRUE;
@@ -47,13 +51,7 @@ __attribute__((visibility("default"))) void js_api_set_persistent_address(
 
   persistent_start = GPOINTER_TO_SIZE(address);
 
-  if (getenv("__AFL_PERSISTENT") == NULL) {
-
-    FATAL(
-        "You must set __AFL_PERSISTENT manually if using persistent mode "
-        "configured using JS");
-
-  }
+  __afl_set_persistent_mode(1);
 
 }
 
@@ -257,3 +255,10 @@ __attribute__((visibility("default"))) void js_api_set_stalker_adjacent_blocks(
 
 }
 
+__attribute__((visibility("default"))) void js_api_set_js_main_hook(
+    const js_main_hook_t hook) {
+
+  js_main_hook = hook;
+
+}
+
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index cb88eabe..913e3a46 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -36,13 +36,13 @@
 extern mach_port_t mach_task_self();
 extern GumAddress  gum_darwin_find_entrypoint(mach_port_t task);
 #else
-extern int  __libc_start_main(int *(main)(int, char **, char **), int argc,
+extern int  __libc_start_main(int (*main)(int, char **, char **), int argc,
                               char **ubp_av, void (*init)(void),
                               void (*fini)(void), void (*rtld_fini)(void),
                               void(*stack_end));
 #endif
 
-typedef int *(*main_fn_t)(int argc, char **argv, char **envp);
+typedef int (*main_fn_t)(int argc, char **argv, char **envp);
 
 static main_fn_t main_fn = NULL;
 
@@ -217,7 +217,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
 
 }
 
-static int *on_main(int argc, char **argv, char **envp) {
+static int on_main(int argc, char **argv, char **envp) {
 
   on_main_os(argc, argv, envp);
 
@@ -225,12 +225,20 @@ static int *on_main(int argc, char **argv, char **envp) {
 
   afl_frida_start();
 
-  return main_fn(argc, argv, envp);
+  if (js_main_hook != NULL) {
+
+    return js_main_hook(argc, argv, envp);
+
+  } else {
+
+    return main_fn(argc, argv, envp);
+
+  }
 
 }
 
 #if defined(EMBEDDED)
-extern int *main(int argc, char **argv, char **envp);
+extern int main(int argc, char **argv, char **envp);
 
 static void intercept_main(void) {
 
@@ -253,7 +261,7 @@ static void intercept_main(void) {
 }
 
 #else
-static int on_libc_start_main(int *(main)(int, char **, char **), int argc,
+static int on_libc_start_main(int (*main)(int, char **, char **), int argc,
                               char **ubp_av, void (*init)(void),
                               void (*fini)(void), void (*rtld_fini)(void),
                               void(*stack_end)) {
diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c
index 902274b3..5425b01b 100644
--- a/frida_mode/src/persistent/persistent_x86.c
+++ b/frida_mode/src/persistent/persistent_x86.c
@@ -4,6 +4,7 @@
 
 #include "instrument.h"
 #include "persistent.h"
+#include "util.h"
 
 #if defined(__i386__)
 
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
index 027417ee..9844c74c 100644
--- a/frida_mode/src/ranges.c
+++ b/frida_mode/src/ranges.c
@@ -166,8 +166,22 @@ static void convert_token(gchar *token, GumMemoryRange *range) {
 
 gint range_sort(gconstpointer a, gconstpointer b) {
 
-  return ((GumMemoryRange *)a)->base_address -
-         ((GumMemoryRange *)b)->base_address;
+  GumMemoryRange *ra = (GumMemoryRange *)a;
+  GumMemoryRange *rb = (GumMemoryRange *)b;
+
+  if (ra->base_address < rb->base_address) {
+
+    return -1;
+
+  } else if (ra->base_address > rb->base_address) {
+
+    return 1;
+
+  } else {
+
+    return 0;
+
+  }
 
 }
 
@@ -249,7 +263,7 @@ static void check_for_overlaps(GArray *array) {
     GumAddress      curr_limit = curr->base_address + curr->size;
     if (prev_limit > curr->base_address) {
 
-      FFATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
+      FFATAL("Overlapping ranges 0x%016" G_GINT64_MODIFIER
              "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
              "x-0x%016" G_GINT64_MODIFIER "x",
              prev->base_address, prev_limit, curr->base_address, curr_limit);
diff --git a/frida_mode/src/seccomp/seccomp_event.c b/frida_mode/src/seccomp/seccomp_event.c
index aca0967a..0907eff8 100644
--- a/frida_mode/src/seccomp/seccomp_event.c
+++ b/frida_mode/src/seccomp/seccomp_event.c
@@ -10,7 +10,13 @@
 
 int seccomp_event_create(void) {
 
+#ifdef SYS_eventfd
   int fd = syscall(SYS_eventfd, 0, 0);
+#else
+# ifdef SYS_eventfd2
+  int fd = syscall(SYS_eventfd2, 0, 0);
+# endif
+#endif
   if (fd < 0) { FFATAL("seccomp_event_create"); }
   return fd;
 
diff --git a/frida_mode/src/seccomp/seccomp_filter.c b/frida_mode/src/seccomp/seccomp_filter.c
index a7c0926c..5aee398f 100644
--- a/frida_mode/src/seccomp/seccomp_filter.c
+++ b/frida_mode/src/seccomp/seccomp_filter.c
@@ -72,7 +72,13 @@ static struct sock_filter filter[] = {
 
     /* Allow us to make anonymous maps */
     BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+#ifdef __NR_mmap
     BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap, 0, 3),
+#else
+# ifdef __NR_mmap2
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap2, 0, 3),
+# endif
+#endif
     BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
              (offsetof(struct seccomp_data, args[4]))),
     BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, -1, 0, 1),
diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c
index 65ed5d50..b4dd5a47 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -111,10 +111,23 @@ void stalker_init(void) {
   }
 
 #endif
-
   if (stalker_ic_entries == 0) { stalker_ic_entries = 32; }
 
-  if (stalker_adjacent_blocks == 0) { stalker_adjacent_blocks = 32; }
+  if (instrument_coverage_filename == NULL) {
+
+    if (stalker_adjacent_blocks == 0) { stalker_adjacent_blocks = 32; }
+
+  } else {
+
+    if (stalker_adjacent_blocks != 0) {
+
+      FFATAL(
+          "AFL_FRIDA_STALKER_ADJACENT_BLOCKS and AFL_FRIDA_INST_COVERAGE_FILE "
+          "are incompatible");
+
+    }
+
+  }
 
 #if defined(__x86_64__) || defined(__i386__)
   stalker = g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries,
diff --git a/frida_mode/test/freetype2/GNUmakefile b/frida_mode/test/freetype2/GNUmakefile
index 891660ca..f7a50de2 100644
--- a/frida_mode/test/freetype2/GNUmakefile
+++ b/frida_mode/test/freetype2/GNUmakefile
@@ -84,7 +84,7 @@ all: $(TEST_BIN)
 	make -C $(ROOT)frida_mode/
 
 32:
-	CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+	CFLAGS="-m32" CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
 
 $(BUILD_DIR):
 	mkdir -p $@
@@ -126,7 +126,7 @@ $(HARNESS_SRC):
 	wget -O $@ $(HARNESS_URL)
 
 $(HARNESS_OBJ): $(HARNESS_SRC)
-	$(CC) -o $@ -c $<
+	$(CC) $(CFLAGS) -o $@ -c $<
 
 ########## TEST #######
 
diff --git a/frida_mode/test/js/GNUmakefile b/frida_mode/test/js/GNUmakefile
index aad81d08..c702ad98 100644
--- a/frida_mode/test/js/GNUmakefile
+++ b/frida_mode/test/js/GNUmakefile
@@ -10,6 +10,7 @@ TESTINSTSRC:=$(PWD)test.c
 TESTINSTBIN2:=$(BUILD_DIR)test2
 TESTINSTSRC2:=$(PWD)test2.c
 
+AFLPP_DRIVER_DUMMY_INPUT:=$(BUILD_DIR)dummy
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
@@ -40,9 +41,36 @@ $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
 $(TESTINSTBIN2): $(TESTINSTSRC2) | $(BUILD_DIR)
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
 
+$(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR)
+	dd if=/dev/zero bs=1048576 count=1 of=$@
+
 clean:
 	rm -rf $(BUILD_DIR)
 
+frida_js_main: $(TESTINSTBIN) $(TEST_DATA_FILE) $(AFLPP_DRIVER_DUMMY_INPUT)
+	AFL_PRELOAD=$(AFL_PRELOAD) \
+	AFL_FRIDA_JS_SCRIPT=main.js \
+	$(ROOT)afl-fuzz \
+		-D \
+		-O \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-t 10000+ \
+		-- \
+			$(TESTINSTBIN) $(AFLPP_DRIVER_DUMMY_INPUT)
+
+frida_js_fuzz: $(TESTINSTBIN) $(TEST_DATA_FILE) $(AFLPP_DRIVER_DUMMY_INPUT)
+	AFL_PRELOAD=$(AFL_PRELOAD) \
+	AFL_FRIDA_JS_SCRIPT=fuzz.js \
+	$(ROOT)afl-fuzz \
+		-D \
+		-O \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-t 10000+ \
+		-- \
+			$(TESTINSTBIN) $(AFLPP_DRIVER_DUMMY_INPUT)
+
 frida_js_entry: $(TESTINSTBIN) $(TEST_DATA_FILE)
 	AFL_PRELOAD=$(AFL_PRELOAD) \
 	AFL_FRIDA_JS_SCRIPT=entry.js \
diff --git a/frida_mode/test/js/fuzz.js b/frida_mode/test/js/fuzz.js
new file mode 100644
index 00000000..24eca2b6
--- /dev/null
+++ b/frida_mode/test/js/fuzz.js
@@ -0,0 +1,41 @@
+Afl.print('******************');
+Afl.print('* AFL FRIDA MODE *');
+Afl.print('******************');
+Afl.print('');
+
+Afl.print(`PID: ${Process.id}`);
+
+const name = Process.enumerateModules()[0].name;
+Afl.print(`Name: ${name}`);
+
+new ModuleMap().values().forEach(m => {
+    Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
+});
+
+const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
+Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);
+
+const cm = new CModule(`
+
+    extern unsigned char * __afl_fuzz_ptr;
+    extern unsigned int * __afl_fuzz_len;
+    extern void LLVMFuzzerTestOneInput(char *buf, int len);
+
+    void My_LLVMFuzzerTestOneInput(char *buf, int len) {
+
+      LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
+
+    }
+    `,
+    {
+        LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
+        __afl_fuzz_ptr: Afl.getAflFuzzPtr(),
+        __afl_fuzz_len: Afl.getAflFuzzLen()
+    });
+
+Afl.setEntryPoint(cm.My_LLVMFuzzerTestOneInput);
+Afl.setPersistentAddress(cm.My_LLVMFuzzerTestOneInput);
+Afl.setInMemoryFuzzing();
+Interceptor.replace(LLVMFuzzerTestOneInput, cm.My_LLVMFuzzerTestOneInput);
+Afl.print("done");
+Afl.done();
diff --git a/frida_mode/test/js/main.js b/frida_mode/test/js/main.js
new file mode 100644
index 00000000..06306fc4
--- /dev/null
+++ b/frida_mode/test/js/main.js
@@ -0,0 +1,44 @@
+Afl.print('******************');
+Afl.print('* AFL FRIDA MODE *');
+Afl.print('******************');
+Afl.print('');
+
+Afl.print(`PID: ${Process.id}`);
+
+const name = Process.enumerateModules()[0].name;
+Afl.print(`Name: ${name}`);
+
+new ModuleMap().values().forEach(m => {
+    Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
+});
+
+const main = DebugSymbol.fromName('main').address;
+Afl.print(`main: ${main}`);
+
+const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
+Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);
+
+const cm = new CModule(`
+
+    extern unsigned char * __afl_fuzz_ptr;
+    extern unsigned int * __afl_fuzz_len;
+    extern void LLVMFuzzerTestOneInput(char *buf, int len);
+
+    int main(int argc, char **argv)  {
+
+      LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
+
+    }
+    `,
+    {
+        LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
+        __afl_fuzz_ptr: Afl.getAflFuzzPtr(),
+        __afl_fuzz_len: Afl.getAflFuzzLen()
+    });
+
+Afl.setEntryPoint(cm.main);
+Afl.setPersistentAddress(cm.main);
+Afl.setInMemoryFuzzing();
+Afl.setJsMainHook(cm.main);
+Afl.print("done");
+Afl.done();
diff --git a/frida_mode/test/png/persistent/hook/GNUmakefile b/frida_mode/test/png/persistent/hook/GNUmakefile
index 23aa94d0..5010662b 100644
--- a/frida_mode/test/png/persistent/hook/GNUmakefile
+++ b/frida_mode/test/png/persistent/hook/GNUmakefile
@@ -144,7 +144,6 @@ frida_entry_slow: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $
 
 frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 	AFL_PRELOAD=$(AFL_PRELOAD) \
-	__AFL_PERSISTENT=1 \
 	AFL_FRIDA_JS_SCRIPT=load.js \
 	$(ROOT)afl-fuzz \
 		-D \
diff --git a/frida_mode/test/vorbis/GNUmakefile b/frida_mode/test/vorbis/GNUmakefile
new file mode 100644
index 00000000..59ae9a59
--- /dev/null
+++ b/frida_mode/test/vorbis/GNUmakefile
@@ -0,0 +1,200 @@
+PWD:=$(shell pwd)/
+ROOT:=$(PWD)../../../
+BUILD_DIR:=$(PWD)build/
+
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
+AFLPP_QEMU_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/qemu_hook.so
+
+OGG_GIT_REPO:=https://github.com/xiph/ogg.git
+OGG_BUILD_DIR:=$(BUILD_DIR)ogg/
+OGG_DIR:=$(OGG_BUILD_DIR)ogg/
+OGG_INSTALL:=$(OGG_BUILD_DIR)install/
+OGG_LIB:=$(OGG_INSTALL)lib/libogg.a
+
+VORBIS_GIT_REPO:=https://github.com/xiph/vorbis.git
+VORBIS_BUILD_DIR:=$(BUILD_DIR)vorbis/
+VORBIS_DIR:=$(VORBIS_BUILD_DIR)vorbis/
+VORBIS_INSTALL:=$(VORBIS_BUILD_DIR)install/
+VORBIS_LIB:=$(VORBIS_INSTALL)lib/libvorbis.a
+VORBISFILE_LIB:=$(VORBIS_INSTALL)lib/libvorbisfile.a
+
+DECODE_URL:=https://raw.githubusercontent.com/google/oss-fuzz/688aadaf44499ddada755562109e5ca5eb3c5662/projects/vorbis/decode_fuzzer.cc
+DECODE_SRC:=$(BUILD_DIR)decode_fuzzer.cc
+DECODE_OBJ:=$(BUILD_DIR)decode_fuzzer.o
+
+HARNESS_URL:=https://raw.githubusercontent.com/llvm/llvm-project/main/compiler-rt/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c
+HARNESS_SRC:=$(BUILD_DIR)StandaloneFuzzTargetMain.c
+HARNESS_OBJ:=$(BUILD_DIR)StandaloneFuzzTargetMain.o
+
+LDFLAGS += -lpthread
+
+TEST_BIN:=$(BUILD_DIR)decode_fuzzer
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie
+endif
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+TEST_DATA_SRC:=https://github.com/google/fuzzbench/blob/master/benchmarks/vorbis-2017-12-11/seeds/sound.ogg?raw=true
+TEST_DATA_FILE:=$(TEST_DATA_DIR)sound.ogg
+DUMMY_DATA_FILE:=$(BUILD_DIR)default_seed
+
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+QEMU_OUT:=$(BUILD_DIR)qemu-out
+
+ifndef ARCH
+
+ARCH=$(shell uname -m)
+ifeq "$(ARCH)" "aarch64"
+ ARCH:=arm64
+endif
+
+ifeq "$(ARCH)" "i686"
+ ARCH:=x86
+endif
+endif
+
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x4000000000)
+
+ifeq "$(ARCH)" "aarch64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
+endif
+
+ifeq "$(ARCH)" "x86_64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000)
+endif
+
+ifeq "$(ARCH)" "x86"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000)
+endif
+
+.PHONY: all clean frida hook
+
+all: $(TEST_BIN)
+	make -C $(ROOT)frida_mode/
+
+32:
+	CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+########## OGG #######
+
+$(OGG_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(OGG_DIR): | $(OGG_BUILD_DIR)
+	git clone $(OGG_GIT_REPO) $@
+	git -C $(OGG_DIR) checkout c8391c2b267a7faf9a09df66b1f7d324e9eb7766
+
+$(OGG_LIB): | $(OGG_DIR)
+	cd $(OGG_DIR) && ./autogen.sh
+	cd $(OGG_DIR) && ./configure \
+		--prefix=$(OGG_INSTALL) \
+		--enable-static \
+		--disable-shared \
+		--disable-crc
+	make -C $(OGG_DIR) install
+
+ogg: $(OGG_LIB)
+
+########## VORBIS #######
+
+$(VORBIS_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(VORBIS_DIR): | $(VORBIS_BUILD_DIR)
+	git clone $(VORBIS_GIT_REPO) $@
+	git -C $(VORBIS_DIR) checkout c1c2831fc7306d5fbd7bc800324efd12b28d327f
+
+$(VORBIS_LIB): $(OGG_LIB) | $(VORBIS_DIR)
+	cd $(VORBIS_DIR) && ./autogen.sh
+	cd $(VORBIS_DIR) && ./configure \
+		--prefix=$(VORBIS_INSTALL) \
+		--enable-static \
+    	--disable-shared \
+		--with-ogg=$(OGG_INSTALL)
+	make -C $(VORBIS_DIR) install
+
+vorbis: $(VORBIS_LIB)
+
+########## HARNESS #######
+
+$(DECODE_SRC):
+	wget -O $@ $(DECODE_URL)
+
+$(DECODE_OBJ): $(DECODE_SRC)
+	$(CXX) -o $@ -c $< -I$(VORBIS_DIR)include/ -I$(OGG_DIR)include/
+
+decode: $(DECODE_OBJ)
+
+########## HARNESS #######
+
+$(HARNESS_SRC):
+	wget -O $@ $(HARNESS_URL)
+
+$(HARNESS_OBJ): $(HARNESS_SRC)
+	$(CC) -o $@ -c $<
+
+harness: $(HARNESS_OBJ)
+
+########## TEST #######
+
+$(TEST_BIN): $(VORBIS_LIB) $(OGG_LIB) $(HARNESS_OBJ) $(DECODE_OBJ)
+	$(CXX) \
+		$(CXXFLAGS) \
+		-std=c++11 \
+		$(DECODE_OBJ) \
+		$(HARNESS_OBJ) \
+		$(VORBISFILE_LIB) \
+     	$(VORBIS_LIB) \
+		$(OGG_LIB) \
+	    -o $@
+
+########## DUMMY #######
+
+$(TEST_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(DUMMY_DATA_FILE): | $(TEST_DATA_DIR)
+	dd if=/dev/zero bs=1048576 count=1 of=$@
+
+###### TEST DATA #######
+
+$(TEST_DATA_FILE):
+	wget -O $@ $(TEST_DATA_SRC)
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE) $(DUMMY_DATA_FILE)
+	AFL_FRIDA_PERSISTENT_CNT=1000000 \
+	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
+	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	$(ROOT)afl-fuzz \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-m none \
+		-d \
+		-O \
+		-V 30 \
+		-- \
+			$(TEST_BIN) $(DUMMY_DATA_FILE)
+
+qemu: $(TEST_BIN) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE) $(DUMMY_DATA_FILE)
+	AFL_QEMU_PERSISTENT_CNT=1000000 \
+	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \
+	AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \
+	AFL_ENTRYPOINT=$(AFL_QEMU_PERSISTENT_ADDR) \
+	$(ROOT)afl-fuzz \
+		-i $(TEST_DATA_DIR) \
+		-o $(QEMU_OUT) \
+		-m none \
+		-d \
+		-Q \
+		-V 30 \
+		-- \
+			$(TEST_BIN) $(DUMMY_DATA_FILE)
diff --git a/frida_mode/test/vorbis/Makefile b/frida_mode/test/vorbis/Makefile
new file mode 100644
index 00000000..07b139e9
--- /dev/null
+++ b/frida_mode/test/vorbis/Makefile
@@ -0,0 +1,13 @@
+all:
+	@echo trying to use GNU make...
+	@gmake all || echo please install GNUmake
+
+32:
+	@echo trying to use GNU make...
+	@gmake 32 || echo please install GNUmake
+
+clean:
+	@gmake clean
+
+frida:
+	@gmake frida
diff --git a/frida_mode/test/vorbis/get_symbol_addr.py b/frida_mode/test/vorbis/get_symbol_addr.py
new file mode 100755
index 00000000..1c46e010
--- /dev/null
+++ b/frida_mode/test/vorbis/get_symbol_addr.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python3
+import argparse
+from elftools.elf.elffile import ELFFile
+
+def process_file(file, symbol, base):
+    with open(file, 'rb') as f:
+        elf = ELFFile(f)
+        symtab = elf.get_section_by_name('.symtab')
+        mains = symtab.get_symbol_by_name(symbol)
+        if len(mains) != 1:
+            print ("Failed to find main")
+            return 1
+
+        main_addr = mains[0]['st_value']
+        main = base + main_addr
+        print ("0x%016x" % main)
+        return 0
+
+def hex_value(x):
+    return int(x, 16)
+
+def main():
+    parser = argparse.ArgumentParser(description='Process some integers.')
+    parser.add_argument('-f', '--file', dest='file', type=str,
+                    help='elf file name', required=True)
+    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
+                    help='symbol name', required=True)
+    parser.add_argument('-b', '--base', dest='base', type=hex_value,
+                    help='elf base address', required=True)
+
+    args = parser.parse_args()
+    return process_file (args.file, args.symbol, args.base)
+
+if __name__ == "__main__":
+    ret = main()
+    exit(ret)
diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts
index e20ad3ec..0473cbf6 100644
--- a/frida_mode/ts/lib/afl.ts
+++ b/frida_mode/ts/lib/afl.ts
@@ -179,6 +179,14 @@ class Afl {
     Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
   }
 
+  /*
+   * Set a callback to be called in place of the usual `main` function. This see
+   * `Scripting.md` for details.
+   */
+  public static setJsMainHook(address: NativePointer): void {
+    Afl.jsApiSetJsMainHook(address);
+  }
+
   /**
    * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
    * `NativePointer` should be provided as it's argument.
@@ -387,6 +395,11 @@ class Afl {
     "void",
     ["pointer"]);
 
+  private static readonly jsApiSetJsMainHook = Afl.jsApiGetFunction(
+    "js_api_set_js_main_hook",
+    "void",
+    ["pointer"]);
+
   private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction(
     "js_api_set_persistent_address",
     "void",
@@ -427,16 +440,16 @@ class Afl {
     "void",
     ["pointer"]);
 
-  private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction(
-    "js_api_set_stalker_callback",
-    "void",
-    ["pointer"]);
-
   private static readonly jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction(
     "js_api_set_stalker_adjacent_blocks",
     "void",
     ["uint32"]);
 
+  private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction(
+    "js_api_set_stalker_callback",
+    "void",
+    ["pointer"]);
+
   private static readonly jsApiSetStalkerIcEntries = Afl.jsApiGetFunction(
     "js_api_set_stalker_ic_entries",
     "void",
diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc
index 4e25221a..37726607 100644
--- a/instrumentation/SanitizerCoverageLTO.so.cc
+++ b/instrumentation/SanitizerCoverageLTO.so.cc
@@ -621,7 +621,6 @@ bool ModuleSanitizerCoverage::instrumentModule(
             bool   isStrncasecmp = true;
             bool   isIntMemcpy = true;
             bool   isStdString = true;
-            bool   addedNull = false;
             size_t optLen = 0;
 
             Function *Callee = callInst->getCalledFunction();
@@ -801,7 +800,6 @@ bool ModuleSanitizerCoverage::instrumentModule(
                   if (literalLength + 1 == optLength) {
 
                     Str2.append("\0", 1);  // add null byte
-                    // addedNull = true;
 
                   }
 
@@ -909,8 +907,8 @@ bool ModuleSanitizerCoverage::instrumentModule(
 
                 if (optLen < 2) { continue; }
                 if (literalLength + 1 == optLen) {  // add null byte
+
                   thestring.append("\0", 1);
-                  addedNull = true;
 
                 }
 
@@ -922,14 +920,18 @@ bool ModuleSanitizerCoverage::instrumentModule(
             // was not already added
             if (!isMemcmp) {
 
-              if (addedNull == false && thestring[optLen - 1] != '\0') {
+              /*
+                            if (addedNull == false && thestring[optLen - 1] !=
+                 '\0') {
 
-                thestring.append("\0", 1);  // add null byte
-                optLen++;
+                              thestring.append("\0", 1);  // add null byte
+                              optLen++;
 
-              }
+                            }
 
-              if (!isStdString) {
+              */
+              if (!isStdString &&
+                  thestring.find('\0', 0) != std::string::npos) {
 
                 // ensure we do not have garbage
                 size_t offset = thestring.find('\0', 0);
diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c
index 759c813a..5d198ada 100644
--- a/instrumentation/afl-compiler-rt.o.c
+++ b/instrumentation/afl-compiler-rt.o.c
@@ -670,7 +670,7 @@ static void __afl_start_snapshots(void) {
 
   u8 child_stopped = 0;
 
-  void (*old_sigchld_handler)(int) = 0;  // = signal(SIGCHLD, SIG_DFL);
+  void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
 
   /* Phone home and tell the parent that we're OK. If parent isn't there,
      assume we're not running in forkserver mode and just execute program. */
@@ -926,7 +926,7 @@ static void __afl_start_forkserver(void) {
 
   u8 child_stopped = 0;
 
-  void (*old_sigchld_handler)(int) = 0;  // = signal(SIGCHLD, SIG_DFL);
+  void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
 
   if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) {
 
@@ -1885,30 +1885,21 @@ static int area_is_valid(void *ptr, size_t len) {
 
 }
 
-void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) {
-
-  /*
-    u32 i;
-    if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return;
-    fprintf(stderr, "rtn_n len=%u arg0=", len);
-    for (i = 0; i < len; i++)
-      fprintf(stderr, "%02x", ptr1[i]);
-    fprintf(stderr, " arg1=");
-    for (i = 0; i < len; i++)
-      fprintf(stderr, "%02x", ptr2[i]);
-    fprintf(stderr, "\n");
-  */
+/* hook for string with length functions, eg. strncmp, strncasecmp etc.
+   Note that we ignore the len parameter and take longer strings if present. */
+void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) {
 
-  if (likely(!__afl_cmp_map)) return;
   // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len);
+  if (likely(!__afl_cmp_map)) return;
   if (unlikely(!len)) return;
-  int l = MIN(31, len);
-
-  if ((l = area_is_valid(ptr1, l)) <= 0 ||
-      (l = area_is_valid(ptr2, l)) <= 0)
-    return;
+  int len0 = MIN(len, 31);
+  int len1 = strnlen(ptr1, len0);
+  if (len1 < 31) len1 = area_is_valid(ptr1, len1 + 1);
+  int len2 = strnlen(ptr2, len0);
+  if (len2 < 31) len2 = area_is_valid(ptr1, len2 + 1);
+  int l = MAX(len1, len2);
+  if (l < 2) return;
 
-  // fprintf(stderr, "RTN2 %u\n", l);
   uintptr_t k = (uintptr_t)__builtin_return_address(0);
   k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1));
 
@@ -1936,27 +1927,25 @@ void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) {
   struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k];
   hits &= CMP_MAP_RTN_H - 1;
 
-  cmpfn[hits].v0_len = l;
-  cmpfn[hits].v1_len = l;
-  __builtin_memcpy(cmpfn[hits].v0, ptr1, l);
-  __builtin_memcpy(cmpfn[hits].v1, ptr2, l);
+  cmpfn[hits].v0_len = 0x80 + l;
+  cmpfn[hits].v1_len = 0x80 + l;
+  __builtin_memcpy(cmpfn[hits].v0, ptr1, len1);
+  __builtin_memcpy(cmpfn[hits].v1, ptr2, len2);
   // fprintf(stderr, "RTN3\n");
 
 }
 
-void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) {
-
-  /*
-    if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return;
-    fprintf(stderr, "rtn_strn len=%u arg0=%s arg1=%s\n", len, ptr1, ptr2);
-  */
+/* hook for string functions, eg. strcmp, strcasecmp etc. */
+void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) {
 
+  // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2);
   if (likely(!__afl_cmp_map)) return;
-  // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len);
-  if (unlikely(!len)) return;
-  int l = MIN(31, len + 1);
+  if (unlikely(!ptr1 || !ptr2)) return;
+  int len1 = strnlen(ptr1, 30) + 1;
+  int len2 = strnlen(ptr2, 30) + 1;
+  int l = MAX(len1, len2);
+  if (l < 3) return;
 
-  // fprintf(stderr, "RTN2 %u\n", l);
   uintptr_t k = (uintptr_t)__builtin_return_address(0);
   k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1));
 
@@ -1984,29 +1973,38 @@ void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) {
   struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k];
   hits &= CMP_MAP_RTN_H - 1;
 
-  cmpfn[hits].v0_len = 0x80 + l;
-  cmpfn[hits].v1_len = 0x80 + l;
-  __builtin_memcpy(cmpfn[hits].v0, ptr1, l);
-  __builtin_memcpy(cmpfn[hits].v1, ptr2, l);
+  cmpfn[hits].v0_len = 0x80 + len1;
+  cmpfn[hits].v1_len = 0x80 + len2;
+  __builtin_memcpy(cmpfn[hits].v0, ptr1, len1);
+  __builtin_memcpy(cmpfn[hits].v1, ptr2, len2);
   // fprintf(stderr, "RTN3\n");
 
 }
 
-void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) {
+/* hook function for all other func(ptr, ptr, ...) variants */
+void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
 
   /*
+    u32 i;
     if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return;
-    fprintf(stderr, "rtn_str arg0=%s arg1=%s\n", ptr1, ptr2);
+    fprintf(stderr, "rtn arg0=");
+    for (i = 0; i < 32; i++)
+      fprintf(stderr, "%02x", ptr1[i]);
+    fprintf(stderr, " arg1=");
+    for (i = 0; i < 32; i++)
+      fprintf(stderr, "%02x", ptr2[i]);
+    fprintf(stderr, "\n");
   */
 
-  if (likely(!__afl_cmp_map)) return;
   // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2);
-  if (unlikely(!ptr1 || !ptr2)) return;
-  int len1 = MIN(31, strlen(ptr1) + 1);
-  int len2 = MIN(31, strlen(ptr2) + 1);
-  int l = MIN(MAX(len1, len2), 31);
+  if (likely(!__afl_cmp_map)) return;
+  int l1, l2;
+  if ((l1 = area_is_valid(ptr1, 31)) <= 0 ||
+      (l2 = area_is_valid(ptr2, 31)) <= 0)
+    return;
+  int len = MIN(31, MIN(l1, l2));
 
-  // fprintf(stderr, "RTN2 %u\n", l);
+  // fprintf(stderr, "RTN2 %u\n", len);
   uintptr_t k = (uintptr_t)__builtin_return_address(0);
   k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1));
 
@@ -2016,16 +2014,16 @@ void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) {
 
     __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
     __afl_cmp_map->headers[k].hits = 1;
-    __afl_cmp_map->headers[k].shape = l - 1;
+    __afl_cmp_map->headers[k].shape = len - 1;
     hits = 0;
 
   } else {
 
     hits = __afl_cmp_map->headers[k].hits++;
 
-    if (__afl_cmp_map->headers[k].shape < l) {
+    if (__afl_cmp_map->headers[k].shape < len) {
 
-      __afl_cmp_map->headers[k].shape = l - 1;
+      __afl_cmp_map->headers[k].shape = len - 1;
 
     }
 
@@ -2034,37 +2032,44 @@ void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) {
   struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k];
   hits &= CMP_MAP_RTN_H - 1;
 
-  cmpfn[hits].v0_len = 0x80 + len1;
-  cmpfn[hits].v1_len = 0x80 + len2;
-  __builtin_memcpy(cmpfn[hits].v0, ptr1, len1);
-  __builtin_memcpy(cmpfn[hits].v1, ptr2, len2);
+  cmpfn[hits].v0_len = len;
+  cmpfn[hits].v1_len = len;
+  __builtin_memcpy(cmpfn[hits].v0, ptr1, len);
+  __builtin_memcpy(cmpfn[hits].v1, ptr2, len);
   // fprintf(stderr, "RTN3\n");
 
 }
 
-void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
+/* hook for func(ptr, ptr, len, ...) looking functions.
+   Note that for the time being we ignore len as this could be wrong
+   information and pass it on to the standard binary rtn hook */
+void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) {
 
+  (void)(len);
+  __cmplog_rtn_hook(ptr1, ptr2);
+
+#if 0
   /*
     u32 i;
     if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return;
-    fprintf(stderr, "rtn arg0=");
-    for (i = 0; i < 32; i++)
+    fprintf(stderr, "rtn_n len=%u arg0=", len);
+    for (i = 0; i < len; i++)
       fprintf(stderr, "%02x", ptr1[i]);
     fprintf(stderr, " arg1=");
-    for (i = 0; i < 32; i++)
+    for (i = 0; i < len; i++)
       fprintf(stderr, "%02x", ptr2[i]);
     fprintf(stderr, "\n");
   */
 
+  // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len);
   if (likely(!__afl_cmp_map)) return;
-  // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2);
-  int l1, l2;
-  if ((l1 = area_is_valid(ptr1, 31)) <= 0 ||
-      (l2 = area_is_valid(ptr2, 31)) <= 0)
+  if (unlikely(!len)) return;
+  int l = MIN(31, len);
+
+  if ((l = area_is_valid(ptr1, l)) <= 0 || (l = area_is_valid(ptr2, l)) <= 0)
     return;
-  int len = MIN(31, MIN(l1, l2));
 
-  // fprintf(stderr, "RTN2 %u\n", len);
+  // fprintf(stderr, "RTN2 %u\n", l);
   uintptr_t k = (uintptr_t)__builtin_return_address(0);
   k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1));
 
@@ -2074,16 +2079,16 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
 
     __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
     __afl_cmp_map->headers[k].hits = 1;
-    __afl_cmp_map->headers[k].shape = len - 1;
+    __afl_cmp_map->headers[k].shape = l - 1;
     hits = 0;
 
   } else {
 
     hits = __afl_cmp_map->headers[k].hits++;
 
-    if (__afl_cmp_map->headers[k].shape < len) {
+    if (__afl_cmp_map->headers[k].shape < l) {
 
-      __afl_cmp_map->headers[k].shape = len - 1;
+      __afl_cmp_map->headers[k].shape = l - 1;
 
     }
 
@@ -2092,11 +2097,12 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
   struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k];
   hits &= CMP_MAP_RTN_H - 1;
 
-  cmpfn[hits].v0_len = len;
-  cmpfn[hits].v1_len = len;
-  __builtin_memcpy(cmpfn[hits].v0, ptr1, len);
-  __builtin_memcpy(cmpfn[hits].v1, ptr2, len);
+  cmpfn[hits].v0_len = l;
+  cmpfn[hits].v1_len = l;
+  __builtin_memcpy(cmpfn[hits].v0, ptr1, l);
+  __builtin_memcpy(cmpfn[hits].v1, ptr2, l);
   // fprintf(stderr, "RTN3\n");
+#endif
 
 }
 
@@ -2247,5 +2253,11 @@ void __afl_coverage_interesting(u8 val, u32 id) {
 
 }
 
+void __afl_set_persistent_mode(u8 mode) {
+
+  is_persistent = mode;
+
+}
+
 #undef write_error
 
diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc
index 7c04c0c5..2ffa8fbb 100644
--- a/instrumentation/afl-llvm-dict2file.so.cc
+++ b/instrumentation/afl-llvm-dict2file.so.cc
@@ -291,7 +291,6 @@ bool AFLdict2filePass::runOnModule(Module &M) {
           bool   isIntMemcpy = true;
           bool   isStdString = true;
           bool   isStrstr = true;
-          bool   addedNull = false;
           size_t optLen = 0;
 
           Function *Callee = callInst->getCalledFunction();
@@ -591,7 +590,6 @@ bool AFLdict2filePass::runOnModule(Module &M) {
               if (optLen < 2) { continue; }
               if (literalLength + 1 == optLen) {  // add null byte
                 thestring.append("\0", 1);
-                addedNull = true;
 
               }
 
@@ -603,18 +601,27 @@ bool AFLdict2filePass::runOnModule(Module &M) {
           // was not already added
           if (!isMemcmp) {
 
-            if (addedNull == false && thestring[optLen - 1] != '\0') {
+            /*
+                        if (addedNull == false && thestring[optLen - 1] != '\0')
+               {
 
-              thestring.append("\0", 1);  // add null byte
-              optLen++;
+                          thestring.append("\0", 1);  // add null byte
+                          optLen++;
 
-            }
+                        }
+
+            */
 
             if (!isStdString) {
 
               // ensure we do not have garbage
               size_t offset = thestring.find('\0', 0);
-              if (offset + 1 < optLen) optLen = offset + 1;
+              if (offset && offset < optLen && offset + 1 < optLen) {
+
+                optLen = offset + 1;
+
+              }
+
               thestring = thestring.substr(0, optLen);
 
             }
diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc
deleted file mode 100644
index cd43b437..00000000
--- a/instrumentation/afl-llvm-lto-instrumentation.so.cc
+++ /dev/null
@@ -1,1119 +0,0 @@
-/*
-   american fuzzy lop++ - LLVM LTO instrumentation pass
-   ----------------------------------------------------
-
-   Written by Marc Heuse <mh@mh-sec.de>
-
-   Copyright 2019-2020 AFLplusplus Project. All rights reserved.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at:
-
-     https://www.apache.org/licenses/LICENSE-2.0
-
-   This library is plugged into LLVM when invoking clang through afl-clang-lto.
-
- */
-
-#define AFL_LLVM_PASS
-
-#include "config.h"
-#include "debug.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include <list>
-#include <memory>
-#include <string>
-#include <fstream>
-#include <set>
-#include <iostream>
-
-#include "llvm/Config/llvm-config.h"
-#include "llvm/ADT/Statistic.h"
-#include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LegacyPassManager.h"
-#include "llvm/IR/BasicBlock.h"
-#include "llvm/IR/Module.h"
-#include "llvm/IR/DebugInfo.h"
-#include "llvm/IR/CFG.h"
-#include "llvm/IR/Verifier.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
-#include "llvm/Transforms/Utils/BasicBlockUtils.h"
-#include "llvm/Analysis/LoopInfo.h"
-#include "llvm/Analysis/MemorySSAUpdater.h"
-#include "llvm/Analysis/ValueTracking.h"
-#include "llvm/Pass.h"
-#include "llvm/IR/Constants.h"
-
-#include "afl-llvm-common.h"
-
-using namespace llvm;
-
-namespace {
-
-class AFLLTOPass : public ModulePass {
-
- public:
-  static char ID;
-
-  AFLLTOPass() : ModulePass(ID) {
-
-    char *ptr;
-
-    if (getenv("AFL_DEBUG")) debug = 1;
-    if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL)
-      if ((afl_global_id = (uint32_t)atoi(ptr)) < 0 ||
-          afl_global_id >= MAP_SIZE)
-        FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %u\n",
-              ptr, MAP_SIZE - 1);
-
-    skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
-
-  }
-
-  void getAnalysisUsage(AnalysisUsage &AU) const override {
-
-    ModulePass::getAnalysisUsage(AU);
-    AU.addRequired<DominatorTreeWrapperPass>();
-    AU.addRequired<LoopInfoWrapperPass>();
-
-  }
-
-  bool runOnModule(Module &M) override;
-
- protected:
-  uint32_t               afl_global_id = 1, autodictionary = 1;
-  uint32_t               function_minimum_size = 1;
-  uint32_t               inst_blocks = 0, inst_funcs = 0, total_instr = 0;
-  unsigned long long int map_addr = 0x10000;
-  const char *           skip_nozero = NULL;
-  const char *           use_threadsafe_counters = nullptr;
-
-};
-
-}  // namespace
-
-bool AFLLTOPass::runOnModule(Module &M) {
-
-  LLVMContext &            C = M.getContext();
-  std::vector<std::string> dictionary;
-  //  std::vector<CallInst *>          calls;
-  DenseMap<Value *, std::string *> valueMap;
-  std::vector<BasicBlock *>        BlockList;
-  std::ofstream                    dFile;
-  char *                           ptr;
-  size_t                           found = 0;
-
-  srand((unsigned int)time(NULL));
-
-  unsigned long long int moduleID =
-      (((unsigned long long int)(rand() & 0xffffffff)) << 32) | getpid();
-
-  IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
-  IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
-  IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
-
-  /* Show a banner */
-  setvbuf(stdout, NULL, _IONBF, 0);
-
-  if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
-
-    SAYF(cCYA "afl-llvm-lto" VERSION cRST
-              " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n");
-
-  } else
-
-    be_quiet = 1;
-
-  use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST");
-
-  if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) {
-
-    dFile.open(ptr, std::ofstream::out | std::ofstream::app);
-    if (!dFile.is_open()) WARNF("Cannot access document file %s", ptr);
-
-  }
-
-  // we make this the default as the fixed map has problems with
-  // defered forkserver, early constructors, ifuncs and maybe more
-  /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/
-  map_addr = 0;
-
-  if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) {
-
-    uint64_t val;
-    if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) {
-
-      map_addr = 0;
-
-    } else if (getenv("AFL_LLVM_MAP_DYNAMIC")) {
-
-      FATAL(
-          "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together");
-
-    } else if (strncmp(ptr, "0x", 2) != 0) {
-
-      map_addr = 0x10000;  // the default
-
-    } else {
-
-      val = strtoull(ptr, NULL, 16);
-      if (val < 0x100 || val > 0xffffffff00000000) {
-
-        FATAL(
-            "AFL_LLVM_MAP_ADDR must be a value between 0x100 and "
-            "0xffffffff00000000");
-
-      }
-
-      map_addr = val;
-
-    }
-
-  }
-
-  if (debug) { fprintf(stderr, "map address is 0x%llx\n", map_addr); }
-
-  /* Get/set the globals for the SHM region. */
-
-  GlobalVariable *AFLMapPtr = NULL;
-  Value *         MapPtrFixed = NULL;
-
-  if (!map_addr) {
-
-    AFLMapPtr =
-        new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
-                           GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
-
-  } else {
-
-    ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
-    MapPtrFixed =
-        ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty));
-
-  }
-
-  ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
-  ConstantInt *One = ConstantInt::get(Int8Ty, 1);
-
-  // This dumps all inialized global strings - might be useful in the future
-  /*
-  for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) {
-
-    GlobalVariable &GV=*G;
-    if (!GV.getName().str().empty()) {
-
-      fprintf(stderr, "Global Variable: %s", GV.getName().str().c_str());
-      if (GV.hasInitializer())
-        if (auto *Val = dyn_cast<ConstantDataArray>(GV.getInitializer()))
-          fprintf(stderr, " Value: \"%s\"", Val->getAsString().str().c_str());
-      fprintf(stderr, "\n");
-
-    }
-
-  }
-
-  */
-
-  scanForDangerousFunctions(&M);
-
-  /* Instrument all the things! */
-
-  int inst_blocks = 0;
-
-  for (auto &F : M) {
-
-    /*For debugging
-    AttributeSet X = F.getAttributes().getFnAttributes();
-    fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n",
-      M.getName().str().c_str(), F.getName().str().c_str(),
-      X.getNumAttributes());
-    */
-
-    if (F.size() < function_minimum_size) continue;
-    if (isIgnoreFunction(&F)) continue;
-
-    // the instrument file list check
-    AttributeList Attrs = F.getAttributes();
-#if LLVM_VERSION_MAJOR < 14
-    if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) {
-
-#else
-    if (Attrs.hasFnAttr(StringRef("skipinstrument"))) {
-
-#endif
-
-      if (debug)
-        fprintf(stderr,
-                "DEBUG: Function %s is not in a source file that was specified "
-                "in the instrument file list\n",
-                F.getName().str().c_str());
-      continue;
-
-    }
-
-    std::vector<BasicBlock *> InsBlocks;
-
-    if (autodictionary) {
-
-      /*  Some implementation notes.
-       *
-       *  We try to handle 3 cases:
-       *  - memcmp("foo", arg, 3) <- literal string
-       *  - static char globalvar[] = "foo";
-       *    memcmp(globalvar, arg, 3) <- global variable
-       *  - char localvar[] = "foo";
-       *    memcmp(locallvar, arg, 3) <- local variable
-       *
-       *  The local variable case is the hardest. We can only detect that
-       *  case if there is no reassignment or change in the variable.
-       *  And it might not work across llvm version.
-       *  What we do is hooking the initializer function for local variables
-       *  (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned
-       *  variable. And if that variable is then used in a compare function
-       *  we use that noted string.
-       *  This seems not to work for tokens that have a size <= 4 :-(
-       *
-       *  - if the compared length is smaller than the string length we
-       *    save the full string. This is likely better for fuzzing but
-       *    might be wrong in a few cases depending on optimizers
-       *
-       *  - not using StringRef because there is a bug in the llvm 11
-       *    checkout I am using which sometimes points to wrong strings
-       *
-       *  Over and out. Took me a full day. damn. mh/vh
-       */
-
-      for (auto &BB : F) {
-
-        for (auto &IN : BB) {
-
-          CallInst *callInst = nullptr;
-          CmpInst * cmpInst = nullptr;
-
-          if ((cmpInst = dyn_cast<CmpInst>(&IN))) {
-
-            Value *      op = cmpInst->getOperand(1);
-            ConstantInt *ilen = dyn_cast<ConstantInt>(op);
-
-            if (ilen && ilen->uge(0xffffffffffffffff) == false) {
-
-              u64 val2 = 0, val = ilen->getZExtValue();
-              u32 len = 0;
-              if (val > 0x10000 && val < 0xffffffff) len = 4;
-              if (val > 0x100000001 && val < 0xffffffffffffffff) len = 8;
-
-              if (len) {
-
-                auto c = cmpInst->getPredicate();
-
-                switch (c) {
-
-                  case CmpInst::FCMP_OGT:  // fall through
-                  case CmpInst::FCMP_OLE:  // fall through
-                  case CmpInst::ICMP_SLE:  // fall through
-                  case CmpInst::ICMP_SGT:
-
-                    // signed comparison and it is a negative constant
-                    if ((len == 4 && (val & 80000000)) ||
-                        (len == 8 && (val & 8000000000000000))) {
-
-                      if ((val & 0xffff) != 1) val2 = val - 1;
-                      break;
-
-                    }
-
-                    // fall through
-
-                  case CmpInst::FCMP_UGT:  // fall through
-                  case CmpInst::FCMP_ULE:  // fall through
-                  case CmpInst::ICMP_UGT:  // fall through
-                  case CmpInst::ICMP_ULE:
-                    if ((val & 0xffff) != 0xfffe) val2 = val + 1;
-                    break;
-
-                  case CmpInst::FCMP_OLT:  // fall through
-                  case CmpInst::FCMP_OGE:  // fall through
-                  case CmpInst::ICMP_SLT:  // fall through
-                  case CmpInst::ICMP_SGE:
-
-                    // signed comparison and it is a negative constant
-                    if ((len == 4 && (val & 80000000)) ||
-                        (len == 8 && (val & 8000000000000000))) {
-
-                      if ((val & 0xffff) != 1) val2 = val - 1;
-                      break;
-
-                    }
-
-                    // fall through
-
-                  case CmpInst::FCMP_ULT:  // fall through
-                  case CmpInst::FCMP_UGE:  // fall through
-                  case CmpInst::ICMP_ULT:  // fall through
-                  case CmpInst::ICMP_UGE:
-                    if ((val & 0xffff) != 1) val2 = val - 1;
-                    break;
-
-                  default:
-                    val2 = 0;
-
-                }
-
-                dictionary.push_back(std::string((char *)&val, len));
-                found++;
-
-                if (val2) {
-
-                  dictionary.push_back(std::string((char *)&val2, len));
-                  found++;
-
-                }
-
-              }
-
-            }
-
-          }
-
-          if ((callInst = dyn_cast<CallInst>(&IN))) {
-
-            bool   isStrcmp = true;
-            bool   isMemcmp = true;
-            bool   isStrncmp = true;
-            bool   isStrcasecmp = true;
-            bool   isStrncasecmp = true;
-            bool   isIntMemcpy = true;
-            bool   isStdString = true;
-            bool   addedNull = false;
-            size_t optLen = 0;
-
-            Function *Callee = callInst->getCalledFunction();
-            if (!Callee) continue;
-            if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
-            std::string FuncName = Callee->getName().str();
-
-            isStrcmp &= (!FuncName.compare("strcmp") ||
-                         !FuncName.compare("xmlStrcmp") ||
-                         !FuncName.compare("xmlStrEqual") ||
-                         !FuncName.compare("g_strcmp0") ||
-                         !FuncName.compare("curl_strequal") ||
-                         !FuncName.compare("strcsequal"));
-            isMemcmp &=
-                (!FuncName.compare("memcmp") || !FuncName.compare("bcmp") ||
-                 !FuncName.compare("CRYPTO_memcmp") ||
-                 !FuncName.compare("OPENSSL_memcmp") ||
-                 !FuncName.compare("memcmp_const_time") ||
-                 !FuncName.compare("memcmpct"));
-            isStrncmp &= (!FuncName.compare("strncmp") ||
-                          !FuncName.compare("xmlStrncmp") ||
-                          !FuncName.compare("curl_strnequal"));
-            isStrcasecmp &= (!FuncName.compare("strcasecmp") ||
-                             !FuncName.compare("stricmp") ||
-                             !FuncName.compare("ap_cstr_casecmp") ||
-                             !FuncName.compare("OPENSSL_strcasecmp") ||
-                             !FuncName.compare("xmlStrcasecmp") ||
-                             !FuncName.compare("g_strcasecmp") ||
-                             !FuncName.compare("g_ascii_strcasecmp") ||
-                             !FuncName.compare("Curl_strcasecompare") ||
-                             !FuncName.compare("Curl_safe_strcasecompare") ||
-                             !FuncName.compare("cmsstrcasecmp"));
-            isStrncasecmp &= (!FuncName.compare("strncasecmp") ||
-                              !FuncName.compare("strnicmp") ||
-                              !FuncName.compare("ap_cstr_casecmpn") ||
-                              !FuncName.compare("OPENSSL_strncasecmp") ||
-                              !FuncName.compare("xmlStrncasecmp") ||
-                              !FuncName.compare("g_ascii_strncasecmp") ||
-                              !FuncName.compare("Curl_strncasecompare") ||
-                              !FuncName.compare("g_strncasecmp"));
-            isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64");
-            isStdString &=
-                ((FuncName.find("basic_string") != std::string::npos &&
-                  FuncName.find("compare") != std::string::npos) ||
-                 (FuncName.find("basic_string") != std::string::npos &&
-                  FuncName.find("find") != std::string::npos));
-
-            /* we do something different here, putting this BB and the
-               successors in a block map */
-            if (!FuncName.compare("__afl_persistent_loop")) {
-
-              BlockList.push_back(&BB);
-              /*
-                            for (succ_iterator SI = succ_begin(&BB), SE =
-                 succ_end(&BB); SI != SE; ++SI) {
-
-                              BasicBlock *succ = *SI;
-                              BlockList.push_back(succ);
-
-                            }
-
-              */
-
-            }
-
-            if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
-                !isStrncasecmp && !isIntMemcpy && !isStdString)
-              continue;
-
-            /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function
-             * prototype */
-            FunctionType *FT = Callee->getFunctionType();
-
-            isStrcmp &= FT->getNumParams() == 2 &&
-                        FT->getReturnType()->isIntegerTy(32) &&
-                        FT->getParamType(0) == FT->getParamType(1) &&
-                        FT->getParamType(0) ==
-                            IntegerType::getInt8PtrTy(M.getContext());
-            isStrcasecmp &= FT->getNumParams() == 2 &&
-                            FT->getReturnType()->isIntegerTy(32) &&
-                            FT->getParamType(0) == FT->getParamType(1) &&
-                            FT->getParamType(0) ==
-                                IntegerType::getInt8PtrTy(M.getContext());
-            isMemcmp &= FT->getNumParams() == 3 &&
-                        FT->getReturnType()->isIntegerTy(32) &&
-                        FT->getParamType(0)->isPointerTy() &&
-                        FT->getParamType(1)->isPointerTy() &&
-                        FT->getParamType(2)->isIntegerTy();
-            isStrncmp &= FT->getNumParams() == 3 &&
-                         FT->getReturnType()->isIntegerTy(32) &&
-                         FT->getParamType(0) == FT->getParamType(1) &&
-                         FT->getParamType(0) ==
-                             IntegerType::getInt8PtrTy(M.getContext()) &&
-                         FT->getParamType(2)->isIntegerTy();
-            isStrncasecmp &= FT->getNumParams() == 3 &&
-                             FT->getReturnType()->isIntegerTy(32) &&
-                             FT->getParamType(0) == FT->getParamType(1) &&
-                             FT->getParamType(0) ==
-                                 IntegerType::getInt8PtrTy(M.getContext()) &&
-                             FT->getParamType(2)->isIntegerTy();
-            isStdString &= FT->getNumParams() >= 2 &&
-                           FT->getParamType(0)->isPointerTy() &&
-                           FT->getParamType(1)->isPointerTy();
-
-            if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
-                !isStrncasecmp && !isIntMemcpy && !isStdString)
-              continue;
-
-            /* is a str{n,}{case,}cmp/memcmp, check if we have
-             * str{case,}cmp(x, "const") or str{case,}cmp("const", x)
-             * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..)
-             * memcmp(x, "const", ..) or memcmp("const", x, ..) */
-            Value *Str1P = callInst->getArgOperand(0),
-                  *Str2P = callInst->getArgOperand(1);
-            std::string Str1, Str2;
-            StringRef   TmpStr;
-            bool        HasStr1;
-            getConstantStringInfo(Str1P, TmpStr);
-            if (TmpStr.empty()) {
-
-              HasStr1 = false;
-
-            } else {
-
-              HasStr1 = true;
-              Str1 = TmpStr.str();
-
-            }
-
-            bool HasStr2;
-            getConstantStringInfo(Str2P, TmpStr);
-            if (TmpStr.empty()) {
-
-              HasStr2 = false;
-
-            } else {
-
-              HasStr2 = true;
-              Str2 = TmpStr.str();
-
-            }
-
-            if (debug)
-              fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n",
-                      FuncName.c_str(), Str1P, Str1P->getName().str().c_str(),
-                      Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P,
-                      Str2P->getName().str().c_str(), Str2.c_str(),
-                      HasStr2 == true ? "true" : "false");
-
-            // we handle the 2nd parameter first because of llvm memcpy
-            if (!HasStr2) {
-
-              auto *Ptr = dyn_cast<ConstantExpr>(Str2P);
-              if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
-
-                if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
-
-                  if (Var->hasInitializer()) {
-
-                    if (auto *Array = dyn_cast<ConstantDataArray>(
-                            Var->getInitializer())) {
-
-                      HasStr2 = true;
-                      Str2 = Array->getRawDataValues().str();
-
-                    }
-
-                  }
-
-                }
-
-              }
-
-            }
-
-            // for the internal memcpy routine we only care for the second
-            // parameter and are not reporting anything.
-            if (isIntMemcpy == true) {
-
-              if (HasStr2 == true) {
-
-                Value *      op2 = callInst->getArgOperand(2);
-                ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
-                if (ilen) {
-
-                  uint64_t literalLength = Str2.size();
-                  uint64_t optLength = ilen->getZExtValue();
-                  if (optLength > literalLength + 1) {
-
-                    optLength = Str2.length() + 1;
-
-                  }
-
-                  if (literalLength + 1 == optLength) {
-
-                    Str2.append("\0", 1);  // add null byte
-                    // addedNull = true;
-
-                  }
-
-                }
-
-                valueMap[Str1P] = new std::string(Str2);
-
-                if (debug)
-                  fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P);
-                continue;
-
-              }
-
-              continue;
-
-            }
-
-            // Neither a literal nor a global variable?
-            // maybe it is a local variable that we saved
-            if (!HasStr2) {
-
-              std::string *strng = valueMap[Str2P];
-              if (strng && !strng->empty()) {
-
-                Str2 = *strng;
-                HasStr2 = true;
-                if (debug)
-                  fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(),
-                          Str2P);
-
-              }
-
-            }
-
-            if (!HasStr1) {
-
-              auto Ptr = dyn_cast<ConstantExpr>(Str1P);
-
-              if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
-
-                if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
-
-                  if (Var->hasInitializer()) {
-
-                    if (auto *Array = dyn_cast<ConstantDataArray>(
-                            Var->getInitializer())) {
-
-                      HasStr1 = true;
-                      Str1 = Array->getRawDataValues().str();
-
-                    }
-
-                  }
-
-                }
-
-              }
-
-            }
-
-            // Neither a literal nor a global variable?
-            // maybe it is a local variable that we saved
-            if (!HasStr1) {
-
-              std::string *strng = valueMap[Str1P];
-              if (strng && !strng->empty()) {
-
-                Str1 = *strng;
-                HasStr1 = true;
-                if (debug)
-                  fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(),
-                          Str1P);
-
-              }
-
-            }
-
-            /* handle cases of one string is const, one string is variable */
-            if (!(HasStr1 ^ HasStr2)) continue;
-
-            std::string thestring;
-
-            if (HasStr1)
-              thestring = Str1;
-            else
-              thestring = Str2;
-
-            optLen = thestring.length();
-            if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
-
-            if (isMemcmp || isStrncmp || isStrncasecmp) {
-
-              Value *      op2 = callInst->getArgOperand(2);
-              ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
-
-              if (ilen) {
-
-                uint64_t literalLength = optLen;
-                optLen = ilen->getZExtValue();
-                if (optLen > literalLength + 1) { optLen = literalLength + 1; }
-                if (optLen < 2) { continue; }
-                if (literalLength + 1 == optLen) {  // add null byte
-                  thestring.append("\0", 1);
-                  addedNull = true;
-
-                }
-
-              }
-
-            }
-
-            // add null byte if this is a string compare function and a null
-            // was not already added
-            if (!isMemcmp) {
-
-              if (addedNull == false && thestring[optLen - 1] != '\0') {
-
-                thestring.append("\0", 1);  // add null byte
-                optLen++;
-
-              }
-
-              if (!isStdString) {
-
-                // ensure we do not have garbage
-                size_t offset = thestring.find('\0', 0);
-                if (offset + 1 < optLen) optLen = offset + 1;
-                thestring = thestring.substr(0, optLen);
-
-              }
-
-            }
-
-            if (!be_quiet) {
-
-              fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen,
-                      thestring.length());
-              for (uint8_t i = 0; i < thestring.length(); i++) {
-
-                uint8_t c = thestring[i];
-                if (c <= 32 || c >= 127)
-                  fprintf(stderr, "\\x%02x", c);
-                else
-                  fprintf(stderr, "%c", c);
-
-              }
-
-              fprintf(stderr, "\"\n");
-
-            }
-
-            // we take the longer string, even if the compare was to a
-            // shorter part. Note that depending on the optimizer of the
-            // compiler this can be wrong, but it is more likely that this
-            // is helping the fuzzer
-            if (optLen != thestring.length()) optLen = thestring.length();
-            if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA;
-            if (optLen < MIN_AUTO_EXTRA)  // too short? skip
-              continue;
-
-            dictionary.push_back(thestring.substr(0, optLen));
-
-          }
-
-        }
-
-      }
-
-    }
-
-    for (auto &BB : F) {
-
-      if (F.size() == 1) {
-
-        InsBlocks.push_back(&BB);
-        continue;
-
-      }
-
-      uint32_t succ = 0;
-      for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE;
-           ++SI)
-        if ((*SI)->size() > 0) succ++;
-      if (succ < 2)  // no need to instrument
-        continue;
-
-      if (BlockList.size()) {
-
-        int skip = 0;
-        for (uint32_t k = 0; k < BlockList.size(); k++) {
-
-          if (&BB == BlockList[k]) {
-
-            if (debug)
-              fprintf(stderr,
-                      "DEBUG: Function %s skipping BB with/after __afl_loop\n",
-                      F.getName().str().c_str());
-            skip = 1;
-
-          }
-
-        }
-
-        if (skip) continue;
-
-      }
-
-      InsBlocks.push_back(&BB);
-
-    }
-
-    if (InsBlocks.size() > 0) {
-
-      uint32_t i = InsBlocks.size();
-
-      do {
-
-        --i;
-        BasicBlock *              newBB = NULL;
-        BasicBlock *              origBB = &(*InsBlocks[i]);
-        std::vector<BasicBlock *> Successors;
-        Instruction *             TI = origBB->getTerminator();
-        uint32_t                  fs = origBB->getParent()->size();
-        uint32_t                  countto;
-
-        for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB);
-             SI != SE; ++SI) {
-
-          BasicBlock *succ = *SI;
-          Successors.push_back(succ);
-
-        }
-
-        if (fs == 1) {
-
-          newBB = origBB;
-          countto = 1;
-
-        } else {
-
-          if (TI == NULL || TI->getNumSuccessors() < 2) continue;
-          countto = Successors.size();
-
-        }
-
-        // if (Successors.size() != TI->getNumSuccessors())
-        //  FATAL("Different successor numbers %lu <-> %u\n", Successors.size(),
-        //        TI->getNumSuccessors());
-
-        for (uint32_t j = 0; j < countto; j++) {
-
-          if (fs != 1) newBB = llvm::SplitEdge(origBB, Successors[j]);
-
-          if (!newBB) {
-
-            if (!be_quiet) WARNF("Split failed!");
-            continue;
-
-          }
-
-          if (dFile.is_open()) {
-
-            dFile << "ModuleID=" << moduleID
-                  << " Function=" << F.getName().str()
-                  << " edgeID=" << afl_global_id << "\n";
-
-          }
-
-          BasicBlock::iterator IP = newBB->getFirstInsertionPt();
-          IRBuilder<>          IRB(&(*IP));
-
-          /* Set the ID of the inserted basic block */
-
-          ConstantInt *CurLoc = ConstantInt::get(Int32Ty, afl_global_id++);
-
-          /* Load SHM pointer */
-
-          Value *MapPtrIdx;
-
-          if (map_addr) {
-
-            MapPtrIdx = IRB.CreateGEP(MapPtrFixed, CurLoc);
-
-          } else {
-
-            LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
-            MapPtr->setMetadata(M.getMDKindID("nosanitize"),
-                                MDNode::get(C, None));
-            MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc);
-
-          }
-
-          /* Update bitmap */
-
-          if (use_threadsafe_counters) {
-
-            IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One,
-#if LLVM_VERSION_MAJOR >= 13
-                                llvm::MaybeAlign(1),
-#endif
-                                llvm::AtomicOrdering::Monotonic);
-
-          } else {
-
-            LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
-            Counter->setMetadata(M.getMDKindID("nosanitize"),
-                                 MDNode::get(C, None));
-
-            Value *Incr = IRB.CreateAdd(Counter, One);
-
-            if (skip_nozero == NULL) {
-
-              auto cf = IRB.CreateICmpEQ(Incr, Zero);
-              auto carry = IRB.CreateZExt(cf, Int8Ty);
-              Incr = IRB.CreateAdd(Incr, carry);
-
-            }
-
-            IRB.CreateStore(Incr, MapPtrIdx)
-                ->setMetadata(M.getMDKindID("nosanitize"),
-                              MDNode::get(C, None));
-
-          }
-
-          // done :)
-
-          inst_blocks++;
-
-        }
-
-      } while (i > 0);
-
-    }
-
-  }
-
-  if (dFile.is_open()) dFile.close();
-
-  // save highest location ID to global variable
-  // do this after each function to fail faster
-  if (!be_quiet && afl_global_id > MAP_SIZE &&
-      afl_global_id > FS_OPT_MAX_MAPSIZE) {
-
-    uint32_t pow2map = 1, map = afl_global_id;
-    while ((map = map >> 1))
-      pow2map++;
-    WARNF(
-        "We have %u blocks to instrument but the map size is only %u. Either "
-        "edit config.h and set MAP_SIZE_POW2 from %d to %u, then recompile "
-        "afl-fuzz and llvm_mode and then make this target - or set "
-        "AFL_MAP_SIZE with at least size %u when running afl-fuzz with this "
-        "target.",
-        afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id);
-
-  }
-
-  if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) {
-
-    // yes we could create our own function, insert it into ctors ...
-    // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o
-
-    Function *f = M.getFunction("__afl_auto_init_globals");
-
-    if (!f) {
-
-      fprintf(stderr,
-              "Error: init function could not be found (this should not "
-              "happen)\n");
-      exit(-1);
-
-    }
-
-    BasicBlock *bb = &f->getEntryBlock();
-    if (!bb) {
-
-      fprintf(stderr,
-              "Error: init function does not have an EntryBlock (this should "
-              "not happen)\n");
-      exit(-1);
-
-    }
-
-    BasicBlock::iterator IP = bb->getFirstInsertionPt();
-    IRBuilder<>          IRB(&(*IP));
-
-    if (map_addr) {
-
-      GlobalVariable *AFLMapAddrFixed = new GlobalVariable(
-          M, Int64Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_map_addr");
-      ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
-      StoreInst *  StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed);
-      StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"),
-                                MDNode::get(C, None));
-
-    }
-
-    if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) {
-
-      uint32_t write_loc = (((afl_global_id + 63) >> 6) << 6);
-
-      GlobalVariable *AFLFinalLoc = new GlobalVariable(
-          M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc");
-      ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc);
-      StoreInst *  StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
-      StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
-                                 MDNode::get(C, None));
-
-    }
-
-    if (dictionary.size()) {
-
-      size_t memlen = 0, count = 0;
-
-      // sort and unique the dictionary
-      std::sort(dictionary.begin(), dictionary.end());
-      auto last = std::unique(dictionary.begin(), dictionary.end());
-      dictionary.erase(last, dictionary.end());
-
-      for (auto token : dictionary) {
-
-        memlen += token.length();
-        count++;
-
-      }
-
-      if (!be_quiet)
-        printf("AUTODICTIONARY: %zu string%s found\n", count,
-               count == 1 ? "" : "s");
-
-      if (count) {
-
-        auto ptrhld = std::unique_ptr<char[]>(new char[memlen + count]);
-
-        count = 0;
-
-        size_t offset = 0;
-        for (auto token : dictionary) {
-
-          if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) {
-
-            ptrhld.get()[offset++] = (uint8_t)token.length();
-            memcpy(ptrhld.get() + offset, token.c_str(), token.length());
-            offset += token.length();
-            count++;
-
-          }
-
-        }
-
-        GlobalVariable *AFLDictionaryLen =
-            new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage,
-                               0, "__afl_dictionary_len");
-        ConstantInt *const_len = ConstantInt::get(Int32Ty, offset);
-        StoreInst *StoreDictLen = IRB.CreateStore(const_len, AFLDictionaryLen);
-        StoreDictLen->setMetadata(M.getMDKindID("nosanitize"),
-                                  MDNode::get(C, None));
-
-        ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset);
-        GlobalVariable *AFLInternalDictionary = new GlobalVariable(
-            M, ArrayTy, true, GlobalValue::ExternalLinkage,
-            ConstantDataArray::get(C,
-                                   *(new ArrayRef<char>(ptrhld.get(), offset))),
-            "__afl_internal_dictionary");
-        AFLInternalDictionary->setInitializer(ConstantDataArray::get(
-            C, *(new ArrayRef<char>(ptrhld.get(), offset))));
-        AFLInternalDictionary->setConstant(true);
-
-        GlobalVariable *AFLDictionary = new GlobalVariable(
-            M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage,
-            0, "__afl_dictionary");
-
-        Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero);
-        Value *AFLDictPtr =
-            IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0));
-        StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary);
-        StoreDict->setMetadata(M.getMDKindID("nosanitize"),
-                               MDNode::get(C, None));
-
-      }
-
-    }
-
-  }
-
-  /* Say something nice. */
-
-  if (!be_quiet) {
-
-    if (!inst_blocks)
-      WARNF("No instrumentation targets found.");
-    else {
-
-      char modeline[100];
-      snprintf(modeline, sizeof(modeline), "%s%s%s%s%s",
-               getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
-               getenv("AFL_USE_ASAN") ? ", ASAN" : "",
-               getenv("AFL_USE_MSAN") ? ", MSAN" : "",
-               getenv("AFL_USE_CFISAN") ? ", CFISAN" : "",
-               getenv("AFL_USE_UBSAN") ? ", UBSAN" : "");
-      OKF("Instrumented %d locations with no collisions (on average %llu "
-          "collisions would be in afl-gcc/vanilla AFL) (%s mode).",
-          inst_blocks, calculateCollisions(inst_blocks), modeline);
-
-    }
-
-  }
-
-  return true;
-
-}
-
-char AFLLTOPass::ID = 0;
-
-static void registerAFLLTOPass(const PassManagerBuilder &,
-                               legacy::PassManagerBase &PM) {
-
-  PM.add(new AFLLTOPass());
-
-}
-
-static RegisterPass<AFLLTOPass> X("afl-lto", "afl++ LTO instrumentation pass",
-                                  false, false);
-
-static RegisterStandardPasses RegisterAFLLTOPass(
-    PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerAFLLTOPass);
-
diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc
index 21ce0cf9..9430644e 100644
--- a/instrumentation/afl-llvm-pass.so.cc
+++ b/instrumentation/afl-llvm-pass.so.cc
@@ -45,12 +45,21 @@ typedef long double max_align_t;
 #endif
 
 #include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LegacyPassManager.h"
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+  #include "llvm/Passes/PassPlugin.h"
+  #include "llvm/Passes/PassBuilder.h"
+  #include "llvm/IR/PassManager.h"
+#else
+  #include "llvm/IR/LegacyPassManager.h"
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/MathExtras.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR >= 14                /* how about stable interfaces? */
+  #include "llvm/Passes/OptimizationLevel.h"
+#endif
 
 #if LLVM_VERSION_MAJOR > 3 || \
     (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
@@ -68,17 +77,30 @@ using namespace llvm;
 
 namespace {
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+class AFLCoverage : public PassInfoMixin<AFLCoverage> {
+
+ public:
+  AFLCoverage() {
+
+#else
 class AFLCoverage : public ModulePass {
 
  public:
   static char ID;
   AFLCoverage() : ModulePass(ID) {
 
+#endif
+
     initInstrumentList();
 
   }
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
+#else
   bool runOnModule(Module &M) override;
+#endif
 
  protected:
   uint32_t    ngram_size = 0;
@@ -92,7 +114,55 @@ class AFLCoverage : public ModulePass {
 
 }  // namespace
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
+llvmGetPassPluginInfo() {
+
+  return {LLVM_PLUGIN_API_VERSION, "AFLCoverage", "v0.1",
+          /* lambda to insert our pass into the pass pipeline. */
+          [](PassBuilder &PB) {
+
+  #if 1
+    #if LLVM_VERSION_MAJOR <= 13
+            using OptimizationLevel = typename PassBuilder::OptimizationLevel;
+    #endif
+            PB.registerOptimizerLastEPCallback(
+                [](ModulePassManager &MPM, OptimizationLevel OL) {
+
+                  MPM.addPass(AFLCoverage());
+
+                });
+
+  /* TODO LTO registration */
+  #else
+            using PipelineElement = typename PassBuilder::PipelineElement;
+            PB.registerPipelineParsingCallback([](StringRef          Name,
+                                                  ModulePassManager &MPM,
+                                                  ArrayRef<PipelineElement>) {
+
+              if (Name == "AFLCoverage") {
+
+                MPM.addPass(AFLCoverage());
+                return true;
+
+              } else {
+
+                return false;
+
+              }
+
+            });
+
+  #endif
+
+          }};
+
+}
+
+#else
+
 char AFLCoverage::ID = 0;
+#endif
 
 /* needed up to 3.9.0 */
 #if LLVM_VERSION_MAJOR == 3 && \
@@ -118,8 +188,15 @@ uint64_t PowerOf2Ceil(unsigned in) {
     (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1)
   #define AFL_HAVE_VECTOR_INTRINSICS 1
 #endif
+
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+PreservedAnalyses AFLCoverage::run(Module &M, ModuleAnalysisManager &MAM) {
+
+#else
 bool AFLCoverage::runOnModule(Module &M) {
 
+#endif
+
   LLVMContext &C = M.getContext();
 
   IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
@@ -133,6 +210,10 @@ bool AFLCoverage::runOnModule(Module &M) {
   u32             rand_seed;
   unsigned int    cur_loc = 0;
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+  auto PA = PreservedAnalyses::all();
+#endif
+
   /* Setup random() so we get Actually Random(TM) outputs from AFL_R() */
   gettimeofday(&tv, &tz);
   rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
@@ -970,10 +1051,15 @@ bool AFLCoverage::runOnModule(Module &M) {
 
   }
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+  return PA;
+#else
   return true;
+#endif
 
 }
 
+#if LLVM_VERSION_MAJOR < 11                         /* use old pass manager */
 static void registerAFLPass(const PassManagerBuilder &,
                             legacy::PassManagerBase &PM) {
 
@@ -986,4 +1072,5 @@ static RegisterStandardPasses RegisterAFLPass(
 
 static RegisterStandardPasses RegisterAFLPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
+#endif
 
diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc
index 80af05f0..f2f0de82 100644
--- a/instrumentation/cmplog-instructions-pass.cc
+++ b/instrumentation/cmplog-instructions-pass.cc
@@ -28,11 +28,16 @@
 #include "llvm/Config/llvm-config.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_MAJOR >= 11
+//  #include "llvm/Passes/PassPlugin.h"
+//  #include "llvm/Passes/PassBuilder.h"
+  #include "llvm/IR/PassManager.h"
+#else
+  #include "llvm/IR/LegacyPassManager.h"
+#endif
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Pass.h"
 #include "llvm/Analysis/ValueTracking.h"
@@ -54,6 +59,15 @@ using namespace llvm;
 
 namespace {
 
+#if LLVM_MAJOR >= 11           /* use new pass manager */
+class CmpLogInstructions : public PassInfoMixin<CmpLogInstructions> {
+ public:
+  CmpLogInstructions() {
+
+    initInstrumentList();
+
+  }
+#else
 class CmpLogInstructions : public ModulePass {
 
  public:
@@ -63,7 +77,11 @@ class CmpLogInstructions : public ModulePass {
     initInstrumentList();
 
   }
+#endif
 
+#if LLVM_MAJOR >= 11           /* use new pass manager */
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
+#else
   bool runOnModule(Module &M) override;
 
 #if LLVM_VERSION_MAJOR < 4
@@ -76,6 +94,7 @@ class CmpLogInstructions : public ModulePass {
     return "cmplog instructions";
 
   }
+#endif
 
  private:
   bool hookInstrs(Module &M);
@@ -84,7 +103,9 @@ class CmpLogInstructions : public ModulePass {
 
 }  // namespace
 
+#if LLVM_MAJOR <= 10           /* use old pass manager */
 char CmpLogInstructions::ID = 0;
+#endif
 
 template <class Iterator>
 Iterator Unique(Iterator first, Iterator last) {
@@ -339,7 +360,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
           }
 
 #if LLVM_MAJOR > 11
-          vector_cnt = tt->getElementCount().getFixedValue();
+          vector_cnt = tt->getElementCount().getKnownMinValue();
           ty0 = tt->getElementType();
 #endif
 
@@ -380,7 +401,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
 
           }
 
-          vector_cnt = tt->getElementCount().getFixedValue();
+          vector_cnt = tt->getElementCount().getKnownMinValue();
           ty1 = ty0 = tt->getElementType();
 #endif
 
@@ -567,7 +588,12 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
 
 }
 
+#if LLVM_MAJOR >= 11           /* use new pass manager */
+PreservedAnalyses CmpLogInstructions::run(Module &               M,
+                                              ModuleAnalysisManager &MAM) {
+#else
 bool CmpLogInstructions::runOnModule(Module &M) {
+#endif
 
   if (getenv("AFL_QUIET") == NULL)
     printf("Running cmplog-instructions-pass by andreafioraldi@gmail.com\n");
@@ -576,10 +602,15 @@ bool CmpLogInstructions::runOnModule(Module &M) {
   hookInstrs(M);
   verifyModule(M);
 
+#if LLVM_MAJOR >= 11           /* use new pass manager */
+  return PreservedAnalyses::all();
+#else
   return true;
+#endif
 
 }
 
+#if LLVM_MAJOR < 11                                 /* use old pass manager */
 static void registerCmpLogInstructionsPass(const PassManagerBuilder &,
                                            legacy::PassManagerBase &PM) {
 
@@ -599,4 +630,4 @@ static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO(
     PassManagerBuilder::EP_FullLinkTimeOptimizationLast,
     registerCmpLogInstructionsPass);
 #endif
-
+#endif
diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc
index a1239040..2eb3d77b 100644
--- a/instrumentation/compare-transform-pass.so.cc
+++ b/instrumentation/compare-transform-pass.so.cc
@@ -26,14 +26,23 @@
 
 #include "llvm/ADT/Statistic.h"
 #include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LegacyPassManager.h"
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+  #include "llvm/Passes/PassPlugin.h"
+  #include "llvm/Passes/PassBuilder.h"
+  #include "llvm/IR/PassManager.h"
+#else
+  #include "llvm/IR/LegacyPassManager.h"
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Pass.h"
 #include "llvm/Analysis/ValueTracking.h"
+#if LLVM_VERSION_MAJOR >= 14                /* how about stable interfaces? */
+  #include "llvm/Passes/OptimizationLevel.h"
+#endif
 
 #if LLVM_VERSION_MAJOR > 3 || \
     (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
@@ -52,28 +61,30 @@ using namespace llvm;
 
 namespace {
 
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+class CompareTransform : public PassInfoMixin<CompareTransform> {
+
+ public:
+  CompareTransform() {
+
+#else
 class CompareTransform : public ModulePass {
 
  public:
   static char ID;
   CompareTransform() : ModulePass(ID) {
 
+#endif
+
     initInstrumentList();
 
   }
 
-  bool runOnModule(Module &M) override;
-
-#if LLVM_VERSION_MAJOR < 4
-  const char *getPassName() const override {
-
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
 #else
-  StringRef      getPassName() const override {
-
+  bool runOnModule(Module &M) override;
 #endif
-    return "transforms compare functions";
-
-  }
 
  private:
   bool transformCmps(Module &M, const bool processStrcmp,
@@ -85,7 +96,54 @@ class CompareTransform : public ModulePass {
 
 }  // namespace
 
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
+llvmGetPassPluginInfo() {
+
+  return {LLVM_PLUGIN_API_VERSION, "comparetransform", "v0.1",
+          /* lambda to insert our pass into the pass pipeline. */
+          [](PassBuilder &PB) {
+
+  #if 1
+    #if LLVM_VERSION_MAJOR <= 13
+            using OptimizationLevel = typename PassBuilder::OptimizationLevel;
+    #endif
+            PB.registerOptimizerLastEPCallback(
+                [](ModulePassManager &MPM, OptimizationLevel OL) {
+
+                  MPM.addPass(CompareTransform());
+
+                });
+
+  /* TODO LTO registration */
+  #else
+            using PipelineElement = typename PassBuilder::PipelineElement;
+            PB.registerPipelineParsingCallback([](StringRef          Name,
+                                                  ModulePassManager &MPM,
+                                                  ArrayRef<PipelineElement>) {
+
+              if (Name == "comparetransform") {
+
+                MPM.addPass(CompareTransform());
+                return true;
+
+              } else {
+
+                return false;
+
+              }
+
+            });
+
+  #endif
+
+          }};
+
+}
+
+#else
 char CompareTransform::ID = 0;
+#endif
 
 bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
                                      const bool processMemcmp,
@@ -384,6 +442,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
     bool        isSizedcmp = false;
     bool        isCaseInsensitive = false;
     Function *  Callee = callInst->getCalledFunction();
+
     if (Callee) {
 
       isMemcmp = Callee->getName().compare("memcmp") == 0;
@@ -445,17 +504,22 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
 
     }
 
-    // add null termination character implicit in c strings
-    if (!isMemcmp && TmpConstStr[TmpConstStr.length() - 1]) {
+    // the following is in general OK, but strncmp is sometimes used in binary
+    // data structures and this can result in crashes :( so it is commented out
+    /*
+        // add null termination character implicit in c strings
+        if (!isMemcmp && TmpConstStr[TmpConstStr.length() - 1]) {
 
-      TmpConstStr.append("\0", 1);
+          TmpConstStr.append("\0", 1);
 
-    }
+        }
+
+    */
 
     // in the unusual case the const str has embedded null
     // characters, the string comparison functions should terminate
     // at the first null
-    if (!isMemcmp) {
+    if (!isMemcmp && TmpConstStr.find('\0') != std::string::npos) {
 
       TmpConstStr.assign(TmpConstStr, 0, TmpConstStr.find('\0') + 1);
 
@@ -592,8 +656,14 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
 
 }
 
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+PreservedAnalyses CompareTransform::run(Module &M, ModuleAnalysisManager &MAM) {
+
+#else
 bool CompareTransform::runOnModule(Module &M) {
 
+#endif
+
   if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL)
     printf(
         "Running compare-transform-pass by laf.intel@gmail.com, extended by "
@@ -601,13 +671,28 @@ bool CompareTransform::runOnModule(Module &M) {
   else
     be_quiet = 1;
 
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+  auto PA = PreservedAnalyses::all();
+#endif
+
   transformCmps(M, true, true, true, true, true);
   verifyModule(M);
 
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+                     /*  if (modified) {
+                   
+                         PA.abandon<XX_Manager>();
+                   
+                       }*/
+
+  return PA;
+#else
   return true;
+#endif
 
 }
 
+#if LLVM_MAJOR < 11                                 /* use old pass manager */
 static void registerCompTransPass(const PassManagerBuilder &,
                                   legacy::PassManagerBase &PM) {
 
@@ -622,8 +707,9 @@ static RegisterStandardPasses RegisterCompTransPass(
 static RegisterStandardPasses RegisterCompTransPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass);
 
-#if LLVM_VERSION_MAJOR >= 11
+  #if LLVM_VERSION_MAJOR >= 11
 static RegisterStandardPasses RegisterCompTransPassLTO(
     PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass);
+  #endif
 #endif
 
diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc
index 7c652ca2..8637398f 100644
--- a/instrumentation/split-compares-pass.so.cc
+++ b/instrumentation/split-compares-pass.so.cc
@@ -1,6 +1,7 @@
 /*
  * Copyright 2016 laf-intel
  * extended for floating point by Heiko Eißfeldt
+ * adapted to new pass manager by Heiko Eißfeldt
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,10 +29,20 @@
 
 #include "llvm/Pass.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/IR/LegacyPassManager.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+
+#if LLVM_MAJOR >= 11
+  #include "llvm/Passes/PassPlugin.h"
+  #include "llvm/Passes/PassBuilder.h"
+  #include "llvm/IR/PassManager.h"
+#else
+  #include "llvm/IR/LegacyPassManager.h"
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/IR/Module.h"
+#if LLVM_VERSION_MAJOR >= 14                /* how about stable interfaces? */
+  #include "llvm/Passes/OptimizationLevel.h"
+#endif
 
 #include "llvm/IR/IRBuilder.h"
 #if LLVM_VERSION_MAJOR > 3 || \
@@ -53,27 +64,31 @@ using namespace llvm;
 
 namespace {
 
+#if LLVM_MAJOR >= 11
+class SplitComparesTransform : public PassInfoMixin<SplitComparesTransform> {
+
+ public:
+  //  static char ID;
+  SplitComparesTransform() : enableFPSplit(0) {
+
+#else
 class SplitComparesTransform : public ModulePass {
 
  public:
   static char ID;
   SplitComparesTransform() : ModulePass(ID), enableFPSplit(0) {
 
+#endif
+
     initInstrumentList();
 
   }
 
-  bool runOnModule(Module &M) override;
-#if LLVM_VERSION_MAJOR >= 4
-  StringRef getPassName() const override {
-
+#if LLVM_MAJOR >= 11
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
 #else
-  const char *getPassName() const override {
-
+  bool runOnModule(Module &M) override;
 #endif
-    return "AFL_SplitComparesTransform";
-
-  }
 
  private:
   int enableFPSplit;
@@ -162,7 +177,54 @@ class SplitComparesTransform : public ModulePass {
 
 }  // namespace
 
+#if LLVM_MAJOR >= 11
+extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
+llvmGetPassPluginInfo() {
+
+  return {LLVM_PLUGIN_API_VERSION, "splitcompares", "v0.1",
+          /* lambda to insert our pass into the pass pipeline. */
+          [](PassBuilder &PB) {
+
+  #if 1
+    #if LLVM_VERSION_MAJOR <= 13
+            using OptimizationLevel = typename PassBuilder::OptimizationLevel;
+    #endif
+            PB.registerOptimizerLastEPCallback(
+                [](ModulePassManager &MPM, OptimizationLevel OL) {
+
+                  MPM.addPass(SplitComparesTransform());
+
+                });
+
+  /* TODO LTO registration */
+  #else
+            using PipelineElement = typename PassBuilder::PipelineElement;
+            PB.registerPipelineParsingCallback([](StringRef          Name,
+                                                  ModulePassManager &MPM,
+                                                  ArrayRef<PipelineElement>) {
+
+              if (Name == "splitcompares") {
+
+                MPM.addPass(SplitComparesTransform());
+                return true;
+
+              } else {
+
+                return false;
+
+              }
+
+            });
+
+  #endif
+
+          }};
+
+}
+
+#else
 char SplitComparesTransform::ID = 0;
+#endif
 
 /// This function splits FCMP instructions with xGE or xLE predicates into two
 /// FCMP instructions with predicate xGT or xLT and EQ
@@ -675,7 +737,7 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
   ReplaceInstWithInst(cmp_inst->getParent()->getInstList(), ii, PN);
 
   // We split the comparison into low and high. If this isn't our target
-  // bitwidth we recursivly split the low and high parts again until we have
+  // bitwidth we recursively split the low and high parts again until we have
   // target bitwidth.
   if ((bitw / 2) > target_bitwidth) {
 
@@ -1316,8 +1378,15 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
 
 }
 
+#if LLVM_MAJOR >= 11
+PreservedAnalyses SplitComparesTransform::run(Module &               M,
+                                              ModuleAnalysisManager &MAM) {
+
+#else
 bool SplitComparesTransform::runOnModule(Module &M) {
 
+#endif
+
   char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW");
   if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
   if (bitw_env) { target_bitwidth = atoi(bitw_env); }
@@ -1327,7 +1396,7 @@ bool SplitComparesTransform::runOnModule(Module &M) {
   if ((isatty(2) && getenv("AFL_QUIET") == NULL) ||
       getenv("AFL_DEBUG") != NULL) {
 
-    errs() << "Split-compare-pass by laf.intel@gmail.com, extended by "
+    errs() << "Split-compare-newpass by laf.intel@gmail.com, extended by "
               "heiko@hexco.de (splitting icmp to "
            << target_bitwidth << " bit)\n";
 
@@ -1339,6 +1408,10 @@ bool SplitComparesTransform::runOnModule(Module &M) {
 
   }
 
+#if LLVM_MAJOR >= 11
+  auto PA = PreservedAnalyses::all();
+#endif
+
   if (enableFPSplit) {
 
     count = splitFPCompares(M);
@@ -1371,7 +1444,16 @@ bool SplitComparesTransform::runOnModule(Module &M) {
 
           auto op0 = CI->getOperand(0);
           auto op1 = CI->getOperand(1);
-          if (!op0 || !op1) { return false; }
+          if (!op0 || !op1) {
+
+#if LLVM_MAJOR >= 11
+            return PA;
+#else
+            return false;
+#endif
+
+          }
+
           auto iTy1 = dyn_cast<IntegerType>(op0->getType());
           if (iTy1 && isa<IntegerType>(op1->getType())) {
 
@@ -1420,10 +1502,29 @@ bool SplitComparesTransform::runOnModule(Module &M) {
 
   }
 
+  if ((isatty(2) && getenv("AFL_QUIET") == NULL) ||
+      getenv("AFL_DEBUG") != NULL) {
+
+    errs() << count << " comparisons found\n";
+
+  }
+
+#if LLVM_MAJOR >= 11
+  /*  if (modified) {
+
+      PA.abandon<XX_Manager>();
+
+    }*/
+
+  return PA;
+#else
   return true;
+#endif
 
 }
 
+#if LLVM_MAJOR < 11                                 /* use old pass manager */
+
 static void registerSplitComparesPass(const PassManagerBuilder &,
                                       legacy::PassManagerBase &PM) {
 
@@ -1437,14 +1538,15 @@ static RegisterStandardPasses RegisterSplitComparesPass(
 static RegisterStandardPasses RegisterSplitComparesTransPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass);
 
-#if LLVM_VERSION_MAJOR >= 11
+  #if LLVM_VERSION_MAJOR >= 11
 static RegisterStandardPasses RegisterSplitComparesTransPassLTO(
     PassManagerBuilder::EP_FullLinkTimeOptimizationLast,
     registerSplitComparesPass);
-#endif
+  #endif
 
 static RegisterPass<SplitComparesTransform> X("splitcompares",
                                               "AFL++ split compares",
                                               true /* Only looks at CFG */,
                                               true /* Analysis Pass */);
+#endif
 
diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc
index 1e32a31d..e0a96ac9 100644
--- a/instrumentation/split-switches-pass.so.cc
+++ b/instrumentation/split-switches-pass.so.cc
@@ -27,14 +27,23 @@
 
 #include "llvm/ADT/Statistic.h"
 #include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LegacyPassManager.h"
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+  #include "llvm/Passes/PassPlugin.h"
+  #include "llvm/Passes/PassBuilder.h"
+  #include "llvm/IR/PassManager.h"
+#else
+  #include "llvm/IR/LegacyPassManager.h"
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Pass.h"
 #include "llvm/Analysis/ValueTracking.h"
+#if LLVM_VERSION_MAJOR >= 14                /* how about stable interfaces? */
+  #include "llvm/Passes/OptimizationLevel.h"
+#endif
 
 #include "llvm/IR/IRBuilder.h"
 #if LLVM_VERSION_MAJOR > 3 || \
@@ -54,29 +63,42 @@ using namespace llvm;
 
 namespace {
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+class SplitSwitchesTransform : public PassInfoMixin<SplitSwitchesTransform> {
+
+ public:
+  SplitSwitchesTransform() {
+
+#else
 class SplitSwitchesTransform : public ModulePass {
 
  public:
   static char ID;
   SplitSwitchesTransform() : ModulePass(ID) {
 
+#endif
     initInstrumentList();
 
   }
 
-  bool runOnModule(Module &M) override;
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
+#else
+  bool      runOnModule(Module &M) override;
 
-#if LLVM_VERSION_MAJOR >= 4
+  #if LLVM_VERSION_MAJOR >= 4
   StringRef getPassName() const override {
 
-#else
+  #else
   const char *getPassName() const override {
 
-#endif
+  #endif
     return "splits switch constructs";
 
   }
 
+#endif
+
   struct CaseExpr {
 
     ConstantInt *Val;
@@ -103,7 +125,54 @@ class SplitSwitchesTransform : public ModulePass {
 
 }  // namespace
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
+llvmGetPassPluginInfo() {
+
+  return {LLVM_PLUGIN_API_VERSION, "splitswitches", "v0.1",
+          /* lambda to insert our pass into the pass pipeline. */
+          [](PassBuilder &PB) {
+
+  #if 1
+    #if LLVM_VERSION_MAJOR <= 13
+            using OptimizationLevel = typename PassBuilder::OptimizationLevel;
+    #endif
+            PB.registerOptimizerLastEPCallback(
+                [](ModulePassManager &MPM, OptimizationLevel OL) {
+
+                  MPM.addPass(SplitSwitchesTransform());
+
+                });
+
+  /* TODO LTO registration */
+  #else
+            using PipelineElement = typename PassBuilder::PipelineElement;
+            PB.registerPipelineParsingCallback([](StringRef          Name,
+                                                  ModulePassManager &MPM,
+                                                  ArrayRef<PipelineElement>) {
+
+              if (Name == "splitswitches") {
+
+                MPM.addPass(SplitSwitchesTransform());
+                return true;
+
+              } else {
+
+                return false;
+
+              }
+
+            });
+
+  #endif
+
+          }};
+
+}
+
+#else
 char SplitSwitchesTransform::ID = 0;
+#endif
 
 /* switchConvert - Transform simple list of Cases into list of CaseRange's */
 BasicBlock *SplitSwitchesTransform::switchConvert(
@@ -415,19 +484,42 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
 
 }
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+PreservedAnalyses SplitSwitchesTransform::run(Module &               M,
+                                              ModuleAnalysisManager &MAM) {
+
+#else
 bool SplitSwitchesTransform::runOnModule(Module &M) {
 
+#endif
+
   if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL)
     printf("Running split-switches-pass by laf.intel@gmail.com\n");
   else
     be_quiet = 1;
+
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+  auto PA = PreservedAnalyses::all();
+#endif
+
   splitSwitches(M);
   verifyModule(M);
 
+#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
+                             /*  if (modified) {
+                           
+                                 PA.abandon<XX_Manager>();
+                           
+                               }*/
+
+  return PA;
+#else
   return true;
+#endif
 
 }
 
+#if LLVM_VERSION_MAJOR < 11                         /* use old pass manager */
 static void registerSplitSwitchesTransPass(const PassManagerBuilder &,
                                            legacy::PassManagerBase &PM) {
 
@@ -442,9 +534,10 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass(
 static RegisterStandardPasses RegisterSplitSwitchesTransPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass);
 
-#if LLVM_VERSION_MAJOR >= 11
+  #if LLVM_VERSION_MAJOR >= 11
 static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO(
     PassManagerBuilder::EP_FullLinkTimeOptimizationLast,
     registerSplitSwitchesTransPass);
+  #endif
 #endif
 
diff --git a/src/afl-cc.c b/src/afl-cc.c
index 8ff241ba..c70f193c 100644
--- a/src/afl-cc.c
+++ b/src/afl-cc.c
@@ -462,11 +462,17 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
       } else {
 
+#if LLVM_MAJOR >= 11                        /* use new pass manager */
+        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
+        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
 
       }
 
@@ -482,11 +488,17 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
       } else {
 
+#if LLVM_MAJOR >= 11                        /* use new pass manager */
+        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
+        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
 
       }
 
@@ -502,11 +514,18 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
       } else {
 
+#if LLVM_MAJOR >= 11
+        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
+        cc_params[cc_par_cnt++] =
+            alloc_printf("-fpass-plugin=%s/split-compares-pass.so", obj_path);
+//        cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager";
+#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
 
       }
 
@@ -536,11 +555,17 @@ static void edit_params(u32 argc, char **argv, char **envp) {
             alloc_printf("%s/cmplog-switches-pass.so", obj_path);
 
         // reuse split switches from laf
+#if LLVM_MAJOR >= 11
+        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
+        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
 
       }
 
@@ -548,10 +573,6 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
     }
 
-#if LLVM_MAJOR >= 13
-    // fuck you llvm 13
-    cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager";
-#endif
 
     if (lto_mode && !have_c) {
 
@@ -566,15 +587,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
       free(ld_path);
 
       cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition";
-
-      if (instrument_mode == INSTRUMENT_CFG ||
-          instrument_mode == INSTRUMENT_PCGUARD)
-        cc_params[cc_par_cnt++] = alloc_printf(
-            "-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path);
-      else
-
-        cc_params[cc_par_cnt++] = alloc_printf(
-            "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path);
+      cc_params[cc_par_cnt++] =
+          alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path);
       cc_params[cc_par_cnt++] = lto_flag;
 
     } else {
@@ -598,6 +612,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
         } else {
 
+    cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager";
           cc_params[cc_par_cnt++] = "-Xclang";
           cc_params[cc_par_cnt++] = "-load";
           cc_params[cc_par_cnt++] = "-Xclang";
@@ -630,10 +645,16 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
       } else {
 
+#if LLVM_MAJOR >= 11
+        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
+        cc_params[cc_par_cnt++] =
+            alloc_printf("-fpass-plugin=%s/afl-llvm-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/afl-llvm-pass.so", obj_path);
+#endif
 
       }
 
@@ -650,11 +671,17 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
       } else {
 
+#if LLVM_MAJOR >= 11
+        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
+        cc_params[cc_par_cnt++] =
+            alloc_printf("-fpass-plugin=%s/cmplog-instructions-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);
+#endif
 
         cc_params[cc_par_cnt++] = "-Xclang";
         cc_params[cc_par_cnt++] = "-load";