about summary refs log tree commit diff
path: root/frida_mode
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode')
-rw-r--r--frida_mode/DEBUGGING.md119
-rw-r--r--frida_mode/GNUmakefile54
-rw-r--r--frida_mode/MapDensity.md96
-rw-r--r--frida_mode/README.md489
-rw-r--r--frida_mode/Scripting.md431
-rw-r--r--frida_mode/frida.map4
-rw-r--r--frida_mode/include/asan.h1
-rw-r--r--frida_mode/include/entry.h3
-rw-r--r--frida_mode/include/instrument.h4
-rw-r--r--frida_mode/include/js.h3
-rw-r--r--frida_mode/include/ranges.h2
-rw-r--r--frida_mode/include/seccomp.h111
-rw-r--r--frida_mode/include/stalker.h4
-rw-r--r--frida_mode/include/util.h31
-rw-r--r--frida_mode/many-linux/Dockerfile4
-rw-r--r--frida_mode/many-linux/GNUmakefile15
-rwxr-xr-xfrida_mode/many-linux/realpath2
-rw-r--r--frida_mode/src/asan/asan.c28
-rw-r--r--frida_mode/src/asan/asan_arm32.c6
-rw-r--r--frida_mode/src/asan/asan_arm64.c6
-rw-r--r--frida_mode/src/asan/asan_x64.c6
-rw-r--r--frida_mode/src/asan/asan_x86.c6
-rw-r--r--frida_mode/src/cmplog/cmplog.c40
-rw-r--r--frida_mode/src/cmplog/cmplog_arm32.c4
-rw-r--r--frida_mode/src/cmplog/cmplog_arm64.c61
-rw-r--r--frida_mode/src/cmplog/cmplog_x64.c60
-rw-r--r--frida_mode/src/cmplog/cmplog_x86.c63
-rw-r--r--frida_mode/src/ctx/ctx_arm32.c5
-rw-r--r--frida_mode/src/ctx/ctx_arm64.c13
-rw-r--r--frida_mode/src/ctx/ctx_x64.c5
-rw-r--r--frida_mode/src/ctx/ctx_x86.c5
-rw-r--r--frida_mode/src/entry.c44
-rw-r--r--frida_mode/src/instrument/instrument.c61
-rw-r--r--frida_mode/src/instrument/instrument_arm32.c20
-rw-r--r--frida_mode/src/instrument/instrument_arm64.c5
-rw-r--r--frida_mode/src/instrument/instrument_coverage.c123
-rw-r--r--frida_mode/src/instrument/instrument_debug.c32
-rw-r--r--frida_mode/src/instrument/instrument_x64.c427
-rw-r--r--frida_mode/src/instrument/instrument_x86.c208
-rw-r--r--frida_mode/src/intercept.c5
-rw-r--r--frida_mode/src/js/api.js29
-rw-r--r--frida_mode/src/js/js.c21
-rw-r--r--frida_mode/src/js/js_api.c35
-rw-r--r--frida_mode/src/lib/lib.c55
-rw-r--r--frida_mode/src/lib/lib_apple.c20
-rw-r--r--frida_mode/src/main.c57
-rw-r--r--frida_mode/src/output.c13
-rw-r--r--frida_mode/src/persistent/persistent.c39
-rw-r--r--frida_mode/src/persistent/persistent_arm32.c6
-rw-r--r--frida_mode/src/persistent/persistent_arm64.c3
-rw-r--r--frida_mode/src/persistent/persistent_x64.c3
-rw-r--r--frida_mode/src/persistent/persistent_x86.c4
-rw-r--r--frida_mode/src/prefetch.c22
-rw-r--r--frida_mode/src/ranges.c138
-rw-r--r--frida_mode/src/seccomp/seccomp.c140
-rw-r--r--frida_mode/src/seccomp/seccomp_atomic.c12
-rw-r--r--frida_mode/src/seccomp/seccomp_callback.c145
-rw-r--r--frida_mode/src/seccomp/seccomp_child.c31
-rw-r--r--frida_mode/src/seccomp/seccomp_event.c31
-rw-r--r--frida_mode/src/seccomp/seccomp_filter.c87
-rw-r--r--frida_mode/src/seccomp/seccomp_print.c30
-rw-r--r--frida_mode/src/seccomp/seccomp_socket.c35
-rw-r--r--frida_mode/src/seccomp/seccomp_syscall.c15
-rw-r--r--frida_mode/src/stalker.c57
-rw-r--r--frida_mode/src/stats/stats.c21
-rw-r--r--frida_mode/src/stats/stats_arm32.c8
-rw-r--r--frida_mode/src/stats/stats_arm64.c6
-rw-r--r--frida_mode/src/stats/stats_x86_64.c16
-rw-r--r--frida_mode/src/util.c73
-rw-r--r--frida_mode/test/bloaty/GNUmakefile114
-rw-r--r--frida_mode/test/bloaty/Makefile13
-rwxr-xr-xfrida_mode/test/bloaty/get_symbol_addr.py36
-rw-r--r--frida_mode/test/freetype2/GNUmakefile192
-rw-r--r--frida_mode/test/freetype2/Makefile13
-rwxr-xr-xfrida_mode/test/freetype2/get_symbol_addr.py36
-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/libpcap/GNUmakefile2
-rw-r--r--frida_mode/test/perf/GNUmakefile116
-rw-r--r--frida_mode/test/perf/Makefile19
-rw-r--r--frida_mode/test/perf/perf.c105
-rw-r--r--frida_mode/test/unstable/GNUmakefile14
-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.ts49
-rw-r--r--frida_mode/ub1804/Dockerfile6
-rw-r--r--frida_mode/ub1804/GNUmakefile37
-rw-r--r--frida_mode/ub1804/Makefile9
90 files changed, 3621 insertions, 1260 deletions
diff --git a/frida_mode/DEBUGGING.md b/frida_mode/DEBUGGING.md
index 69663510..207a48bf 100644
--- a/frida_mode/DEBUGGING.md
+++ b/frida_mode/DEBUGGING.md
@@ -1,6 +1,8 @@
+# Debugging
+
 If you are using FRIDA mode and have hit some problems, then this guide may help
 you to diagnose any problems you are encountering. This assumes you have
-followed the [osx-lib](#test/osx-lib) example to start fuzzing your target.
+followed the [osx-lib](test/osx-lib) example to start fuzzing your target.
 
 It should be noted that attempting to debug code using gdb which has been
 instrumented in FRIDA is unlikely to be successful since the debugger will be
@@ -10,69 +12,76 @@ you are very familiar with the implementation of Stalker, the instrumented code
 generated by FRIDA is likely to be very difficult to follow. For this reason,
 the following debugging strategies are outlined below.
 
-By convention below all files should be provided with their path (they are
+By convention, all files below should be provided with their path (they are
 omitted for readability) and all items in `<braces>` are placeholders and should
 be replaced accordingly.
 
-# Select your version
+## Select your version
+
 Test with both the `dev` and `stable` branches of AFL++. The `dev` branch should
 have the very latest version containing any fixes for identified issues. The
 `stable` branch is updated less frequently, but equally might avoid a problem if
 a regression has been introduced into the `dev` branch.
 
-# Enable Diagnostic Information
-- Run your target specifying the `AFL_DEBUG_CHILD=1` environment variable. This
-  will print a lot more diagnostic information to the screen when the target
-  starts up. If you have a simple configuration issue then you will likely see a
-  warning or error message in the output.
+## Enable diagnostic information
+
+Run your target specifying the `AFL_DEBUG_CHILD=1` environment variable. This
+will print a lot more diagnostic information to the screen when the target
+starts up. If you have a simple configuration issue, then you will likely see a
+warning or error message in the output.
+
+## Check your test harness
 
-# Check your Test Harness
 If any of the following steps fail, then there is a problem with your test
-harness, or your target library. Since this is running without FRIDA mode or
+harness or your target library. Since this is running without FRIDA mode or
 `afl-fuzz` that greatly reduces the search area for your defect. This is why it
 is *VERY* important to carry out these basic steps first before taking on the
 additional complexity of debugging with FRIDA mode or `afl-fuzz`.
 
 - Run your harness outside of the fuzzer, passing it a representative seed as
   it's input `./harness <input>`.
-- Pass you harness multiple seeds to check that it is stable when running
+- Pass your harness multiple seeds to check that it is stable when running
   multiple tests as it will when running in fork server mode `./harness <input1>
   <intput2>`.
 - Build your test harness with `CFLAGS=-fsanitize=address` and
   `LDFLAGS=-fsanitize=address`. Then run it again with multiple inputs to check
-  for errors (note that when fuzzing your harness should not be built with any
+  for errors (note that when fuzzing, your harness should not be built with any
   sanitizer options).
 
-# Check the Samples
+## Check the samples
+
 FRIDA mode contains a number of different sample targets in the `test` folder.
-Have a look throught these and find one which is similar to your real target.
+Have a look through these and find one which is similar to your real target.
 Check whether you have any issues running the sample target and make sure you
-compare the command line used to launch the sample with that you are using to
-launch your real target very carefully to check for any differences. If possible
-start with one of these samples and gradually make changes one at a time
-re-testing as you go until you have migrated it to run your own target.
+compare the command line used to launch the sample with the one you are using to
+launch your real target very carefully to check for any differences. If
+possible, start with one of these samples and gradually make changes one at a
+time re-testing as you go until you have migrated it to run your own target.
 
-# FRIDA Mode
-## Basic
-First just try running your target with `LD_PRELOAD=afl-frida-trace.so ./harness
- <input>`. An error here means that your defect occurs when running with just
- FRIDA mode and isn't related to `afl-fuzz`.
+## FRIDA mode
+
+### Basic
+
+First, just try running your target with `LD_PRELOAD=afl-frida-trace.so
+ ./harness <input>`. An error here means that your defect occurs when running
+ with just FRIDA mode and isn't related to `afl-fuzz`.
 
 Now you can try commenting out the implementation of `LLVMFuzzerTestOneInput` so
 that the harness doesn't actually run your target library. This may also aid in
 narrowing down the problem.
+
 ```c
 int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size){
     // fpn_crashme(data, size);
     return 0;
 }
-
 ```
 
-## Persistent Mode
+### Persistent mode
+
 If your target is ok running in basic mode, you can try running it in persistent
 mode (if that is the configuration you are having issues with) as follows (again
-outside of afl-fuzz). This time you will want to run it inside a debugger so
+outside of `afl-fuzz`). This time, you will want to run it inside a debugger so
 that you can use the debugger to send the `SIGCONT` signals (by continuing)
 usually sent by `afl-fuzz` on each iteration.
 
@@ -84,13 +93,15 @@ gdb \
   --ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=<entry_address>' \
   --args ./harness <input>
 ```
-Note we have to manually set the `__AFL_PERSISTENT` environment variable which
-is usually passed by `afl-fuzz`.
 
-Note that setting breakpoints etc is likely to interfere with FRIDA and cause
-spurious errors.
+Note:
+- You have to manually set the `__AFL_PERSISTENT` environment variable which is
+  usually passed by `afl-fuzz`.
+- Setting breakpoints etc. is likely to interfere with FRIDA and cause spurious
+  errors.
 
 If this is successful, you can try additionally loading the hook library:
+
 ```bash
 gdb \
   --ex 'set environment __AFL_PERSISTENT=1' \
@@ -100,6 +111,7 @@ gdb \
   --ex 'set environment AFL_FRIDA_PERSISTENT_HOOK=frida_hook.so'
   --args ./harness <input>
 ```
+
 Note that the format of the hook used for FRIDA mode is subtly different to that
 used when running in QEMU mode as shown below. Thus the DSO used for the hook is
 not interchangeable.
@@ -112,12 +124,14 @@ void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
                          uint8_t *input_buf, uint32_t input_buf_len);
 ```
 
-## ASAN
+### ASAN
+
 It is also possible to enable ASAN (if that is the configuration you are having
-issues with) without having to use `afl-fuzz`. This can be done as follows (note
-that the name of the asan DSO may need to be changed depending on your
-platform). Note that the asan DSO must appear first in the `LD_PRELOAD`
-environment variable:
+issues with) without having to use `afl-fuzz`. This can be done as follows:
+
+Note:
+- The name of the asan DSO may need to be changed depending on your platform.
+- The asan DSO must appear first in the `LD_PRELOAD` environment variable.
 
 ```bash
 LD_PRELOAD=libclang_rt.asan-x86_64.so:afl-frida-trace.so \
@@ -132,29 +146,34 @@ DSO from coverage. Failure to do so will result in ASAN attempting to sanitize
 itself and as a result detecting failures when it attempts to update the shadow
 maps.
 
-# Printf
+## Printf
+
 If you have an idea of where things are going wrong for you, then don't be
 scared to add `printf` statements to either AFL++ or FRIDA mode itself to show
 more diagnostic information. Just be sure to set `AFL_DEBUG=1` and
 `AFL_DEBUG_CHILD=1` when you are testing it.
 
-# Core Dumps
-Lastly, if your defect only occurs when using `afl-fuzz` (e.g. when using
-`CMPLOG` which cannot be tested outside of `afl-fuzz` due to it's need for a
+## Core dumps
+
+Lastly, if your defect only occurs when using `afl-fuzz` (e.g., when using
+`CMPLOG` which cannot be tested outside of `afl-fuzz` due to its need for a
 shared memory mapping being created for it to record its data), it is possible
 to enable the creation of a core dump for post-mortem analysis.
 
-Firstly check your `/proc/sys/kernel/core_pattern` configuration is simply set
-to a filename (AFL++ encourages you to set it to the value 'core' in any case
-since it doesn't want any handler applications getting in the way). Next set
-`ulimit -c unlimited` to remove any size limitations for core files. Lastly,
-when you `afl-fuzz` set the environment variable `AFL_DEBUG=1` to enable the
-creation of the `core` file. The file should be created in the working directory
-of the target application. If there is an existing `core` file aleady there,
-then it may not be overwritten.
+Firstly, check if your `/proc/sys/kernel/core_pattern` configuration is set to a
+filename (AFL++ encourages you to set it to the value `core` in any case since
+it doesn't want any handler applications getting in the way).
+
+Next, set `ulimit -c unlimited` to remove any size limitations for core files.
+
+Lastly, when you `afl-fuzz`, set the environment variable `AFL_DEBUG=1` to
+enable the creation of the `core` file. The file should be created in the
+working directory of the target application. If there is an existing `core` file
+already there, then it may not be overwritten.
+
+## Reach out
 
-# Reach out
 Get in touch on discord and ask for help. The groups are pretty active so
 someone may well be able to offer some advice. Better still, if you are able to
-create a minimal reproducer for your problem it will make it easier to diagnose
-the issue.
+create a minimal reproducer for your problem, it will make it easier to diagnose
+the issue.
\ No newline at end of file
diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile
index 3e35e2f6..5303fd1a 100644
--- a/frida_mode/GNUmakefile
+++ b/frida_mode/GNUmakefile
@@ -30,8 +30,7 @@ AFL_CFLAGS:=-Wno-unused-parameter \
 
 LDFLAGS+=-shared \
 		 -lpthread \
-		 -lresolv \
-		 -ldl
+		 -lresolv
 
 ifdef DEBUG
 CFLAGS+=-Werror \
@@ -46,6 +45,11 @@ FRIDA_BUILD_DIR:=$(BUILD_DIR)frida/
 FRIDA_TRACE:=$(BUILD_DIR)afl-frida-trace.so
 FRIDA_TRACE_EMBEDDED:=$(BUILD_DIR)afl-frida-trace-embedded
 
+TARGET_CC?=$(CC)
+TARGET_CXX?=$(CXX)
+HOST_CC?=$(CC)
+HOST_CXX?=$(CXX)
+
 ifndef ARCH
 
 ARCH=$(shell uname -m)
@@ -71,19 +75,45 @@ ifdef DEBUG
 endif
 LDFLAGS+=	-z noexecstack \
 			-Wl,--gc-sections \
-			-Wl,--exclude-libs,ALL
+			-Wl,--exclude-libs,ALL \
+		    -ldl \
+		    -lrt
 LDSCRIPT:=-Wl,--version-script=$(PWD)frida.map
 endif
 
 ifeq "$(shell uname)" "Linux"
  OS:=linux
+ ifneq "$(findstring musl, $(shell ldd --version 2>&1 | head -n 1))" ""
+  CFLAGS+=       -D__MUSL__
+ endif
+endif
+
+ifneq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" ""
+ OS:=android
+ ifneq "$(findstring aarch64, $(shell $(CC) --version 2>/dev/null))" ""
+   ARCH:=arm64
+ endif
+ ifneq "$(findstring arm, $(shell $(CC) --version 2>/dev/null))" ""
+   ARCH:=arm
+ endif
+ ifneq "$(findstring x86_64, $(shell $(CC) --version 2>/dev/null))" ""
+   ARCH:=x86_64
+ endif
+ ifneq "$(findstring i686, $(shell $(CC) --version 2>/dev/null))" ""
+   ARCH:=x86
+ endif
+endif
+
+ifeq "$(ARCH)" "armhf"
+ TARGET_CC:=arm-linux-gnueabihf-gcc
+ TARGET_CXX:=arm-linux-gnueabihf-g++
 endif
 
 ifndef OS
  $(error "Operating system unsupported")
 endif
 
-GUM_DEVKIT_VERSION=15.0.16
+GUM_DEVKIT_VERSION=15.1.13
 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)"
 
@@ -168,7 +198,7 @@ $(GUM_DEVIT_HEADER): $(GUM_DEVKIT_TARBALL)
 
 ############################## AFL #############################################
 $(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC)
-	$(CC) \
+	$(TARGET_CC) \
 		$(CFLAGS) \
 		$(AFL_CFLAGS) \
 		-I $(ROOT) \
@@ -177,7 +207,7 @@ $(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC)
 		-c $<
 
 $(AFL_PERFORMANCE_OBJ): $(AFL_PERFORMANCE_SRC)
-	$(CC) \
+	$(TARGET_CC) \
 		$(CFLAGS) \
 		$(AFL_CFLAGS) \
 		-I $(ROOT) \
@@ -188,13 +218,13 @@ $(AFL_PERFORMANCE_OBJ): $(AFL_PERFORMANCE_SRC)
 ############################### JS #############################################
 
 $(BIN2C): $(BIN2C_SRC)
-	$(CC) -D_GNU_SOURCE -o $@ $<
+	$(HOST_CC) -D_GNU_SOURCE -o $@ $<
 
 $(JS_SRC): $(JS) $(BIN2C)| $(BUILD_DIR)
 	cd $(JS_DIR) && $(BIN2C) api_js $(JS) $@
 
 $(JS_OBJ): $(JS_SRC) GNUmakefile
-	$(CC) \
+	$(TARGET_CC) \
 		$(CFLAGS) \
 		-I $(ROOT)include \
 		-I $(FRIDA_BUILD_DIR) \
@@ -206,7 +236,7 @@ $(JS_OBJ): $(JS_SRC) GNUmakefile
 
 define BUILD_SOURCE
 $(2): $(1) $(INCLUDES) GNUmakefile | $(OBJ_DIR)
-	$(CC) \
+	$(TARGET_CC) \
 		$(CFLAGS) \
 		-I $(ROOT)include \
 		-I $(FRIDA_BUILD_DIR) \
@@ -220,7 +250,7 @@ $(foreach src,$(SOURCES),$(eval $(call BUILD_SOURCE,$(src),$(OBJ_DIR)$(notdir $(
 ######################## AFL-FRIDA-TRACE #######################################
 
 $(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL_COMPILER_RT_OBJ) $(AFL_PERFORMANCE_OBJ) GNUmakefile | $(BUILD_DIR)
-	$(CXX) \
+	$(TARGET_CXX) \
 		$(OBJS) \
 		$(JS_OBJ) \
 		$(GUM_DEVIT_LIBRARY) \
@@ -235,10 +265,10 @@ $(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL
 ############################# HOOK #############################################
 
 $(AFLPP_FRIDA_DRIVER_HOOK_OBJ): $(AFLPP_FRIDA_DRIVER_HOOK_SRC) $(GUM_DEVIT_HEADER) | $(BUILD_DIR)
-	$(CC) $(CFLAGS) $(LDFLAGS) -I $(FRIDA_BUILD_DIR) $< -o $@
+	$(TARGET_CC) $(CFLAGS) $(LDFLAGS) -I $(FRIDA_BUILD_DIR) $< -o $@
 
 $(AFLPP_QEMU_DRIVER_HOOK_OBJ): $(AFLPP_QEMU_DRIVER_HOOK_SRC) | $(BUILD_DIR)
-	$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+	$(TARGET_CC) $(CFLAGS) $(LDFLAGS) $< -o $@
 
 hook: $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QEMU_DRIVER_HOOK_OBJ)
 
diff --git a/frida_mode/MapDensity.md b/frida_mode/MapDensity.md
index f4ae3ace..50f2720f 100644
--- a/frida_mode/MapDensity.md
+++ b/frida_mode/MapDensity.md
@@ -1,8 +1,9 @@
-# Map Density
+# Map density
+
+## How coverage works
 
-# How Coverage Works
 The coverage in AFL++ works by assigning each basic block of code a unique ID
-and during execution when transitioning between blocks (e.g. by calls or jumps)
+and during execution when transitioning between blocks (e.g., by calls or jumps)
 assigning each of these edges an ID based upon the source and destination block
 ID.
 
@@ -13,11 +14,12 @@ A single dimensional cumulative byte array is also constructed where each byte
 again represents an individual edge ID, but this time, the value of the byte
 represents a range of how many times that edge has been traversed.
 
-```1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+```
+`1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+`
 
 The theory is that a new path isn't particularly interesting if an edge has been
 traversed `23` instead of `24` times for example, but is interesting if an edge
-has been traversed for the very first time, or the number of times fits within a different bucket.
+has been traversed for the very first time or the number of times fits within a
+different bucket.
 
 After each run, the count of times each edge is hit is compared to the values in
 the cumulative map and if it is different, then the input is kept as a new seed
@@ -27,19 +29,22 @@ This mechanism is described in greater detail in the seminal
 [paper](https://lcamtuf.coredump.cx/afl/technical_details.txt) on AFL by
 [lcamtuf](https://github.com/lcamtuf).
 
-# Collisions
+## Collisions
+
 In black-box fuzzing, we must assume that control may flow from any block to any
-other block, since we don't know any better. Thus for a target with `n` basic
+other block, since we don't know any better. Thus, for a target with `n` basic
 blocks of code, there are `n * n` potential edges. As we can see, even with a
 small number of edges, a very large map will be required so that we have space
 to fit them all. Even if our target only had `1024` blocks, this would require a
 map containing `1048576` entries (or 1Mb in size).
 
-Whilst this may not seem like a lot of memory, it causes problems for two reasons. Firstly, the processing step after each execution must now process much more
-data, and secondly a map this size is unlikely to fit within the L2 cache of the processor. Since this is a very hot code path, we are likely to pay a very heavy
-performance cost.
+Whilst this may not seem like a lot of memory, it causes problems for two
+reasons. Firstly, the processing step after each execution must now process much
+more data, and secondly, a map this size is unlikely to fit within the L2 cache
+of the processor. Since this is a very hot code path, we are likely to pay a
+very heavy performance cost.
 
-Therefore, we must accept that not all edges can have a unique and that
+Therefore, we must accept that not all edges can have a unique ID and that
 therefore there will be collisions. This means that if the fuzzer finds a new
 path by uncovering an edge which was not previously found, but that the same
 edge ID is used by another edge, then it may go completely unnoticed. This is
@@ -47,15 +52,15 @@ obviously undesirable, but equally if our map is too large, then we will not be
 able to process as many potential inputs in the same time and hence not uncover
 edges for that reason. Thus a careful trade-off of map size must be made.
 
-# Block & Edge Numbering
+## Block & edge numbering
+
 Since the original AFL, blocks and edges have always been numbered in the same
-way as we can see from the following C snippet from the whitepaper.
+way as we can see from the following C snippet from the whitepaper:
 
 ```c
-    cur_location = (block_address >> 4) ^ (block_address << 8);
-    shared_mem[cur_location ^ prev_location]++;
-    prev_location = cur_location >> 1;
-
+cur_location = (block_address >> 4) ^ (block_address << 8);
+shared_mem[cur_location ^ prev_location]++;
+prev_location = cur_location >> 1;
 ```
 
 Each block ID is generated by performing a shift and XOR on its address. Then
@@ -63,36 +68,39 @@ the edge ID is calculated as `E = B ^ (B' >> 1)`. Here, we can make two
 observations. In fact, the edge ID is also masked to ensure it is less than the
 size of the map being used.
 
-## Block IDs
+### Block IDs
+
 Firstly, the block ID doesn't have very good entropy. If we consider the address
 of the block, then whilst each block has a unique ID, it isn't necessarily very
 evenly distributed.
 
-We start with a large address, and need to discard a large number of the bits to
+We start with a large address and need to discard a large number of the bits to
 generate a block ID which is within range. But how do we choose the unique bits
-of the address verus those which are the same for every block? The high bits of
-the address may simply be all `0s` or all `1s` to make the address cannonical,
-the middle portion of the address may be the same for all blocks (since if they
-are all within the same binary, then they will all be adjacent in memory), and
-on some systems, even the low bits may have poor entropy as some use fixed
-length aligned instructions. Then we need to consider that a portion of each
-binary may contain the `.data` or `.bss` sections and so may not contain any
-blocks of code at all.
-
-## Edge IDs
+of the address versus those which are the same for every block? The high bits of
+the address may be all `0s` or all `1s` to make the address canonical, the
+middle portion of the address may be the same for all blocks (since if they are
+all within the same binary, then they will all be adjacent in memory), and on
+some systems, even the low bits may have poor entropy as some use fixed length
+aligned instructions. Then we need to consider that a portion of each binary may
+contain the `.data` or `.bss` sections and so may not contain any blocks of code
+at all.
+
+### Edge IDs
+
 Secondly, we can observe that when we generate an edge ID from the source and
 destination block IDs, we perform a right shift on the source block ID. Whilst
 there are good reasons as set out in the whitepaper why such a transform is
-applied, in so doing, we dispose of `1` bit of precious entropy in our source
+applied, in doing so, we dispose of `1` bit of precious entropy in our source
 block ID.
 
 All together, this means that some edge IDs may be more popular than others.
-This means that some portions of the map may be very densly populated with large
-numbers of edges, whilst others may be very sparsely populated, or not populated
-at all.
+This means that some portions of the map may be very densely populated with
+large numbers of edges, whilst others may be very sparsely populated, or not
+populated at all.
 
-# Improvements
-One of the main reaons why this algorithm selected, is performance. All of the
+## Improvements
+
+One of the main reasons why this algorithm selected, is performance. All of the
 operations are very quick to perform and given we may be carrying this out for
 every block of code we execute, performance is critical.
 
@@ -106,23 +114,25 @@ only need to generate this ID once per block and so this ID generation no longer
 needs to be as performant. We can therefore use a hash algorithm to generate
 this ID and therefore ensure that the block IDs are more evenly distributed.
 
-Edge IDs however, can only be determined at run-time. Since we don't know which
+Edge IDs, however, can only be determined at run-time. Since we don't know which
 blocks a given input will traverse until we run it. However, given our block IDs
 are now evenly distributed, generating an evenly distributed edge ID becomes
-simple. Here, the only change we make is to use a rotate operation rather than
-a shift operation so we don't lose a bit of entropy from the source ID.
+simple. Here, the only change we make is to use a rotate operation rather than a
+shift operation so we don't lose a bit of entropy from the source ID.
 
 So our new algorithm becomes:
+
 ```c
-    cur_location = hash(block_address)
-    shared_mem[cur_location ^ prev_location]++;
-    prev_location = rotate(cur_location, 1);
+cur_location = hash(block_address)
+shared_mem[cur_location ^ prev_location]++;
+prev_location = rotate(cur_location, 1);
 ```
 
 Lastly, in the original design, the `cur_location` was always set to `0`, at the
 beginning of a run, we instead set the value of `cur_location` to `hash(0)`.
 
-# Parallel Fuzzing
+## Parallel fuzzing
+
 Another sub-optimal aspect of the original design is that no matter how many
 instances of the fuzzer you ran in parallel, each instance numbered each block
 and so each edge with the same ID. Each instance would therefore find the same
@@ -144,4 +154,4 @@ If only a single new edge is found, and the new path is shared with an instance
 for which that edge collides, that instance may disregard it as irrelevant. In
 practice, however, the discovery of a single new edge, likely leads to several
 more edges beneath it also being found and therefore the likelihood of all of
-these being collisions is very slim.
+these being collisions is very slim.
\ No newline at end of file
diff --git a/frida_mode/README.md b/frida_mode/README.md
index 165f8089..08f6b891 100644
--- a/frida_mode/README.md
+++ b/frida_mode/README.md
@@ -1,38 +1,34 @@
-# FRIDA MODE
+# FRIDA mode
 
 The purpose of FRIDA mode is to provide an alternative binary only fuzzer for
-AFL just like that provided by QEMU mode. The intention is to provide a very
+AFL++ just like that provided by QEMU mode. The intention is to provide a very
 similar user experience, right down to the options provided through environment
 variables.
 
-Whilst AFLplusplus already has some support for running on FRIDA [here](https://github.com/AFLplusplus/AFLplusplus/tree/stable/utils/afl_frida)
-this requires the code to be fuzzed to be provided as a shared library, it
-cannot be used to fuzz executables. Additionally, it requires the user to write
-a small harness around their target code of interest.
-FRIDA mode instead takes a different approach to avoid these limitations.
-In Frida mode binary programs are instrumented, similarly to QEMU mode.
+In FRIDA mode, binary programs are instrumented, similarly to QEMU mode.
 
-## Current Progress
+## Current progress
 
-As FRIDA mode is new, it is missing a lot of features. The design is such that it
-should be possible to add these features in a similar manner to QEMU mode and
+As FRIDA mode is new, it is missing a lot of features. The design is such that
+it should be possible to add these features in a similar manner to QEMU mode and
 perhaps leverage some of its design and implementation.
 
-  | Feature/Instrumentation  | frida-mode | Notes                                        |
-  | -------------------------|:----------:|:--------------------------------------------:|
-  | NeverZero                |     x      |                                              |
-  | Persistent Mode          |     x      | (x86/x64/aarch64 only)                       |
-  | LAF-Intel / CompCov      |     -      | (CMPLOG is better 90% of the time)           |
-  | CMPLOG                   |     x      | (x86/x64/aarch64 only)                       |
-  | Selective Instrumentation|     x      |                                              |
-  | Non-Colliding Coverage   |     -      | (Not possible in binary-only instrumentation |
-  | Ngram prev_loc Coverage  |     -      |                                              |
-  | Context Coverage         |     -      |                                              |
-  | Auto Dictionary          |     -      |                                              |
-  | Snapshot LKM Support     |     -      |                                              |
-  | In-Memory Test Cases     |     x      | (x86/x64/aarch64 only)                       |
+| Feature/Instrumentation  | FRIDA mode | Notes                                         |
+| -------------------------|:----------:|:---------------------------------------------:|
+| NeverZero                |     x      |                                               |
+| Persistent Mode          |     x      | (x86/x64/aarch64 only)                        |
+| LAF-Intel / CompCov      |     -      | (CMPLOG is better 90% of the time)            |
+| CMPLOG                   |     x      | (x86/x64/aarch64 only)                        |
+| Selective Instrumentation|     x      |                                               |
+| Non-Colliding Coverage   |     -      | (not possible in binary-only instrumentation) |
+| Ngram prev_loc Coverage  |     -      |                                               |
+| Context Coverage         |     -      |                                               |
+| Auto Dictionary          |     -      |                                               |
+| Snapshot LKM Support     |     -      |                                               |
+| In-Memory Test Cases     |     x      | (x86/x64/aarch64 only)                        |
 
 ## Compatibility
+
 Currently FRIDA mode supports Linux and macOS targets on both x86/x64
 architecture and aarch64. Later releases may add support for aarch32 and Windows
 targets as well as embedded linux environments.
@@ -41,46 +37,58 @@ FRIDA has been used on various embedded targets using both uClibc and musl C
 runtime libraries, so porting should be possible. However, the current build
 system does not support cross compilation.
 
-## Getting Started
+## Getting started
 
-To build everything run `make`. To build for x86 run `make 32`. Note that in
+To build everything, run `make`. To build for x86, run `make 32`. Note that in
 x86 bit mode, it is not necessary for afl-fuzz to be built for 32-bit. However,
-the shared library for frida_mode must be since it is injected into the target
+the shared library for FRIDA mode must be since it is injected into the target
 process.
 
 Various tests can be found in subfolders within the `test/` directory. To use
-these, first run `make` to build any dependencies. Then run `make qemu` or
-`make frida` to run on either QEMU of FRIDA mode respectively. To run frida
-tests in 32-bit mode, run `make ARCH=x86 frida`. When switching between
-architectures it may be necessary to run `make clean` first for a given build
-target to remove previously generated binaries for a different architecture.
+these, first run `make` to build any dependencies. Then run `make qemu` or `make
+frida` to run on either QEMU of FRIDA mode respectively. To run frida tests in
+32-bit mode, run `make ARCH=x86 frida`. When switching between architectures, it
+may be necessary to run `make clean` first for a given build target to remove
+previously generated binaries for a different architecture.
+
+### Android
+
+In order to build, you need to download the Android SDK:
+
+[https://developer.android.com/ndk/downloads](https://developer.android.com/ndk/downloads)
+
+Then creating locally a standalone chain as follows:
+
+[https://developer.android.com/ndk/guides/standalone_toolchain](https://developer.android.com/ndk/guides/standalone_toolchain)
 
 ## Usage
 
-FRIDA mode added some small modifications to `afl-fuzz` and similar tools
-in AFLplusplus. The intention was that it behaves identically to QEMU, but it uses
-the 'O' switch rather than 'Q'. Whilst the options 'f', 'F', 's' or 'S' may have
+FRIDA mode added some small modifications to `afl-fuzz` and similar tools in
+AFL++. The intention was that it behaves identically to QEMU, but it uses the
+'O' switch rather than 'Q'. Whilst the options 'f', 'F', 's' or 'S' may have
 made more sense for a mode powered by FRIDA Stalker, they were all taken, so
-instead we use 'O' in hommage to the [author](https://github.com/oleavr) of
+instead we use 'O' in homage to the [author](https://github.com/oleavr) of
 FRIDA.
 
 Similarly, the intention is to mimic the use of environment variables used by
-QEMU where possible (by replacing `s/QEMU/FRIDA/g`). Accordingly, the
-following options are currently supported:
+QEMU where possible (by replacing `s/QEMU/FRIDA/g`). Accordingly, the following
+options are currently supported:
 
-* `AFL_FRIDA_DEBUG_MAPS` - See `AFL_QEMU_DEBUG_MAPS`
-* `AFL_FRIDA_EXCLUDE_RANGES` - See `AFL_QEMU_EXCLUDE_RANGES`
-* `AFL_FRIDA_INST_RANGES` - See `AFL_QEMU_INST_RANGES`
-* `AFL_FRIDA_PERSISTENT_ADDR` - See `AFL_QEMU_PERSISTENT_ADDR`
-* `AFL_FRIDA_PERSISTENT_CNT` - See `AFL_QEMU_PERSISTENT_CNT`
-* `AFL_FRIDA_PERSISTENT_HOOK` - See `AFL_QEMU_PERSISTENT_HOOK`
-* `AFL_FRIDA_PERSISTENT_RET` - See `AFL_QEMU_PERSISTENT_RET`
+* `AFL_FRIDA_DEBUG_MAPS` - See `AFL_QEMU_DEBUG_MAPS`.
+* `AFL_FRIDA_EXCLUDE_RANGES` - See `AFL_QEMU_EXCLUDE_RANGES`.
+* `AFL_FRIDA_INST_RANGES` - See `AFL_QEMU_INST_RANGES`.
+* `AFL_FRIDA_PERSISTENT_ADDR` - See `AFL_QEMU_PERSISTENT_ADDR`.
+* `AFL_FRIDA_PERSISTENT_CNT` - See `AFL_QEMU_PERSISTENT_CNT`.
+* `AFL_FRIDA_PERSISTENT_HOOK` - See `AFL_QEMU_PERSISTENT_HOOK`.
+* `AFL_FRIDA_PERSISTENT_RET` - See `AFL_QEMU_PERSISTENT_RET`.
 
 To enable the powerful CMPLOG mechanism, set `-c 0` for `afl-fuzz`.
 
 ## Scripting
 
-One of the more powerful features of FRIDA mode is it's support for configuration by JavaScript, rather than using environment variables. For details of how this works see [here](Scripting.md).
+One of the more powerful features of FRIDA mode is it's support for
+configuration by JavaScript, rather than using environment variables. For
+details of how this works, see [Scripting.md](Scripting.md).
 
 ## Performance
 
@@ -104,16 +112,18 @@ FRIDA mode is supported by using `LD_PRELOAD` (`DYLD_INSERT_LIBRARIES` on macOS)
 to inject a shared library (`afl-frida-trace.so`) into the target. This shared
 library is built using the [frida-gum](https://github.com/frida/frida-gum)
 devkit from the [FRIDA](https://github.com/frida/frida) project. One of the
-components of frida-gum is [Stalker](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8),
+components of frida-gum is
+[Stalker](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8),
 this allows the dynamic instrumentation of running code for AARCH32, AARCH64,
 x86 and x64 architectures. Implementation details can be found
 [here](https://frida.re/docs/stalker/).
 
 Dynamic instrumentation is used to augment the target application with similar
 coverage information to that inserted by `afl-gcc` or `afl-clang`. The shared
-library is also linked to the `compiler-rt` component of AFLplusplus to feedback
-this coverage information to AFL++ and also provide a fork server. It also makes
-use of the FRIDA [prefetch](https://github.com/frida/frida-gum/blob/56dd9ba3ee9a5511b4b0c629394bf122775f1ab7/gum/gumstalker.h#L115)
+library is also linked to the `compiler-rt` component of AFL++ to feedback this
+coverage information to AFL++ and also provide a fork server. It also makes use
+of the FRIDA
+[prefetch](https://github.com/frida/frida-gum/blob/56dd9ba3ee9a5511b4b0c629394bf122775f1ab7/gum/gumstalker.h#L115)
 support to feedback instrumented blocks from the child to the parent using a
 shared memory region to avoid the need to regenerate instrumented blocks on each
 fork.
@@ -131,210 +141,239 @@ instances run CMPLOG mode and instrumentation of the binary is less frequent
 (only on CMP, SUB and CALL instructions) performance is not quite so critical.
 
 ## Advanced configuration options
-* `AFL_FRIDA_INST_COVERAGE_FILE` - File to write DynamoRio format coverage
-information (e.g. to be loaded within IDA lighthouse).
+
+* `AFL_FRIDA_DRIVER_NO_HOOK` - See `AFL_QEMU_DRIVER_NO_HOOK`. When using the
+  QEMU driver to provide a `main` loop for a user provided
+  `LLVMFuzzerTestOneInput`, this option configures the driver to read input from
+  `stdin` rather than using in-memory test cases.
+* `AFL_FRIDA_INST_COVERAGE_FILE` - File to write DynamoRIO format coverage
+  information (e.g., to be loaded within IDA lighthouse).
 * `AFL_FRIDA_INST_DEBUG_FILE` - File to write raw assembly of original blocks
-and their instrumented counterparts during block compilation.
-```
-***
-
-Creating block for 0x7ffff7953313:
-        0x7ffff7953313  mov qword ptr [rax], 0
-        0x7ffff795331a  add rsp, 8
-        0x7ffff795331e  ret
-
-Generated block 0x7ffff75e98e2
-        0x7ffff75e98e2  mov qword ptr [rax], 0
-        0x7ffff75e98e9  add rsp, 8
-        0x7ffff75e98ed  lea rsp, [rsp - 0x80]
-        0x7ffff75e98f5  push rcx
-        0x7ffff75e98f6  movabs rcx, 0x7ffff795331e
-        0x7ffff75e9900  jmp 0x7ffff75e9384
-
-
-***
-```
+  and their instrumented counterparts during block compilation.
+
+  ```
+  ***
+
+  Creating block for 0x7ffff7953313:
+          0x7ffff7953313  mov qword ptr [rax], 0
+          0x7ffff795331a  add rsp, 8
+          0x7ffff795331e  ret
+
+  Generated block 0x7ffff75e98e2
+          0x7ffff75e98e2  mov qword ptr [rax], 0
+          0x7ffff75e98e9  add rsp, 8
+          0x7ffff75e98ed  lea rsp, [rsp - 0x80]
+          0x7ffff75e98f5  push rcx
+          0x7ffff75e98f6  movabs rcx, 0x7ffff795331e
+          0x7ffff75e9900  jmp 0x7ffff75e9384
+
+  ***
+  ```
+
 * `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
-code. Code is considered to be JIT if the executable segment is not backed by a
-file.
+  code. Code is considered to be JIT if the executable segment is not backed by
+  a file.
 * `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
-instrumentation (the default where available). Required to use
-`AFL_FRIDA_INST_TRACE`.
-* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default the child will
-report instrumented blocks back to the parent so that it can also instrument
-them and they be inherited by the next child on fork, implies
-`AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`.
+  instrumentation (the default where available). Required to use
+  `AFL_FRIDA_INST_TRACE`.
+* `AFL_FRIDA_INST_NO_BACKPATCH` - Disable backpatching. At the end of executing
+  each block, control will return to FRIDA to identify the next block to
+  execute.
+* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default, the child will
+  report instrumented blocks back to the parent so that it can also instrument
+  them and they be inherited by the next child on fork, implies
+  `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`.
 * `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH` - Disable prefetching of stalker
-backpatching information. By default the child will report applied backpatches
-to the parent so that they can be applied and then be inherited by the next
-child on fork.
+  backpatching information. By default, the child will report applied
+  backpatches to the parent so that they can be applied and then be inherited by
+  the next child on fork.
 * `AFL_FRIDA_INST_SEED` - Sets the initial seed for the hash function used to
-generate block (and hence edge) IDs. Setting this to a constant value may be
-useful for debugging purposes, e.g. investigating unstable edges.
-* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks,
-implies `AFL_FRIDA_INST_NO_OPTIMIZE`.
+  generate block (and hence edge) IDs. Setting this to a constant value may be
+  useful for debugging purposes, e.g., investigating unstable edges.
+* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks, implies
+  `AFL_FRIDA_INST_NO_OPTIMIZE`.
 * `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge
-is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
-* `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE` - File to write DynamoRio format
-coverage information for unstable edges (e.g. to be loaded within IDA
-lighthouse).
+  is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
+* `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE` - File to write DynamoRIO format
+  coverage information for unstable edges (e.g., to be loaded within IDA
+  lighthouse).
+* `AFL_FRIDA_JS_SCRIPT` - Set the script to be loaded by the FRIDA scripting
+  engine. See [Scipting.md](Scripting.md) for details.
 * `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target
-application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`)
+  application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`).
 * `AFL_FRIDA_OUTPUT_STDERR` - Redirect the standard error of the target
-application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`)
+  application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`).
 * `AFL_FRIDA_PERSISTENT_DEBUG` - Insert a Breakpoint into the instrumented code
-at `AFL_FRIDA_PERSISTENT_HOOK` and `AFL_FRIDA_PERSISTENT_RET` to allow the user
-to detect issues in the persistent loop using a debugger.
-
-```
+  at `AFL_FRIDA_PERSISTENT_HOOK` and `AFL_FRIDA_PERSISTENT_RET` to allow the
+  user to detect issues in the persistent loop using a debugger.
+
+  ```
+  gdb \
+      --ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=XXXXXXXXXX' \
+      --ex 'set environment AFL_FRIDA_PERSISTENT_RET=XXXXXXXXXX' \
+      --ex 'set environment AFL_FRIDA_PERSISTENT_DEBUG=1' \
+      --ex 'set environment AFL_DEBUG_CHILD=1' \
+      --ex 'set environment LD_PRELOAD=afl-frida-trace.so' \
+      --args <my-executable> [my arguments]
+  ```
 
-gdb \
-		--ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=XXXXXXXXXX' \
-		--ex 'set environment AFL_FRIDA_PERSISTENT_RET=XXXXXXXXXX' \
-		--ex 'set environment AFL_FRIDA_PERSISTENT_DEBUG=1' \
-		--ex 'set environment AFL_DEBUG_CHILD=1' \
-		--ex 'set environment LD_PRELOAD=afl-frida-trace.so' \
-		--args <my-executable> [my arguments]
-
-```
 * `AFL_FRIDA_SECCOMP_FILE` - Write a log of any syscalls made by the target to
-the specified file.
+  the specified file.
+* `AFL_FRIDA_STALKER_ADJACENT_BLOCKS` - Configure the number of adjacent blocks
+  to fetch when generating instrumented code. By fetching blocks in the same
+  order they appear in the original program, rather than the order of execution
+  should help reduce locality and adjacency. This includes allowing us to vector
+  between adjacent blocks using a NOP slide rather than an immediate branch.
 * `AFL_FRIDA_STALKER_IC_ENTRIES` - Configure the number of inline cache entries
-stored along-side branch instructions which provide a cache to avoid having to
-call back into FRIDA to find the next block. Default is 32.
+  stored along-side branch instructions which provide a cache to avoid having to
+  call back into FRIDA to find the next block. Default is 32.
 * `AFL_FRIDA_STATS_FILE` - Write statistics information about the code being
-instrumented to the given file name. The statistics are written only for the
-child process when new block is instrumented (when the
-`AFL_FRIDA_STATS_INTERVAL` has expired). Note that simply because a new path is
-found does not mean a new block needs to be compiled. It could simply be that
-the existing blocks instrumented have been executed in a different order.
-```
-stats
------
-Time                  2021-07-21 11:45:49
-Elapsed                                 1 seconds
-
-
-Transitions                    cumulative               delta
------------                    ----------               -----
-total                              753619               17645
-call_imm                             9193 ( 1.22%)        344 ( 1.95%) [       344/s]
-call_reg                                0 ( 0.00%)          0 ( 0.00%) [         0/s]
-call_mem                                0 ( 0.00%)          0 ( 0.00%) [         0/s]
-ret_slow_path                       67974 ( 9.02%)       2988 (16.93%) [      2988/s]
-post_call_invoke                     7996 ( 1.06%)        299 ( 1.69%) [       299/s]
-excluded_call_imm                    3804 ( 0.50%)        200 ( 1.13%) [       200/s]
-jmp_imm                              5445 ( 0.72%)        255 ( 1.45%) [       255/s]
-jmp_reg                             42081 ( 5.58%)       1021 ( 5.79%) [      1021/s]
-jmp_mem                            578092 (76.71%)      10956 (62.09%) [     10956/s]
-jmp_cond_imm                        38951 ( 5.17%)       1579 ( 8.95%) [      1579/s]
-jmp_cond_mem                            0 ( 0.00%)          0 ( 0.00%) [         0/s]
-jmp_cond_reg                            0 ( 0.00%)          0 ( 0.00%) [         0/s]
-jmp_cond_jcxz                           0 ( 0.00%)          0 ( 0.00%) [         0/s]
-jmp_continuation                       84 ( 0.01%)          3 ( 0.02%) [         3/s]
-
-
-Instrumentation
----------------
-Instructions                         7907
-Blocks                               1764
-Avg Instructions / Block                4
-
-
-EOB Instructions
-----------------
-Total                                1763 (22.30%)
-Call Immediates                       358 ( 4.53%)
-Call Immediates Excluded               74 ( 0.94%)
-Call Register                           0 ( 0.00%)
-Call Memory                             0 ( 0.00%)
-Jump Immediates                       176 ( 2.23%)
-Jump Register                           8 ( 0.10%)
-Jump Memory                            10 ( 0.13%)
-Conditional Jump Immediates          1051 (13.29%)
-Conditional Jump CX Immediate           0 ( 0.00%)
-Conditional Jump Register               0 ( 0.00%)
-Conditional Jump Memory                 0 ( 0.00%)
-Returns                               160 ( 2.02%)
-
-
-Relocated Instructions
-----------------------
-Total                                 232 ( 2.93%)
-addsd                                   2 ( 0.86%)
-cmp                                    46 (19.83%)
-comisd                                  2 ( 0.86%)
-divsd                                   2 ( 0.86%)
-divss                                   2 ( 0.86%)
-lea                                   142 (61.21%)
-mov                                    32 (13.79%)
-movsd                                   2 ( 0.86%)
-ucomisd                                 2 ( 0.86%)
-```
-* `AFL_FRIDA_STATS_INTERVAL` - The maximum frequency to output statistics
-information. Stats will be written whenever they are updated if the given
-interval has elapsed since last time they were written.
+  instrumented to the given file name. The statistics are written only for the
+  child process when new block is instrumented (when the
+  `AFL_FRIDA_STATS_INTERVAL` has expired). Note that just because a new path is
+  found does not mean a new block needs to be compiled. It could be that the
+  existing blocks instrumented have been executed in a different order.
+
+  ```
+  stats
+  -----
+  Time                  2021-07-21 11:45:49
+  Elapsed                                 1 seconds
+
+
+  Transitions                    cumulative               delta
+  -----------                    ----------               -----
+  total                              753619               17645
+  call_imm                             9193 ( 1.22%)        344 ( 1.95%) [       344/s]
+  call_reg                                0 ( 0.00%)          0 ( 0.00%) [         0/s]
+  call_mem                                0 ( 0.00%)          0 ( 0.00%) [         0/s]
+  ret_slow_path                       67974 ( 9.02%)       2988 (16.93%) [      2988/s]
+  post_call_invoke                     7996 ( 1.06%)        299 ( 1.69%) [       299/s]
+  excluded_call_imm                    3804 ( 0.50%)        200 ( 1.13%) [       200/s]
+  jmp_imm                              5445 ( 0.72%)        255 ( 1.45%) [       255/s]
+  jmp_reg                             42081 ( 5.58%)       1021 ( 5.79%) [      1021/s]
+  jmp_mem                            578092 (76.71%)      10956 (62.09%) [     10956/s]
+  jmp_cond_imm                        38951 ( 5.17%)       1579 ( 8.95%) [      1579/s]
+  jmp_cond_mem                            0 ( 0.00%)          0 ( 0.00%) [         0/s]
+  jmp_cond_reg                            0 ( 0.00%)          0 ( 0.00%) [         0/s]
+  jmp_cond_jcxz                           0 ( 0.00%)          0 ( 0.00%) [         0/s]
+  jmp_continuation                       84 ( 0.01%)          3 ( 0.02%) [         3/s]
+
+
+  Instrumentation
+  ---------------
+  Instructions                         7907
+  Blocks                               1764
+  Avg Instructions / Block                4
+
+
+  EOB Instructions
+  ----------------
+  Total                                1763 (22.30%)
+  Call Immediates                       358 ( 4.53%)
+  Call Immediates Excluded               74 ( 0.94%)
+  Call Register                           0 ( 0.00%)
+  Call Memory                             0 ( 0.00%)
+  Jump Immediates                       176 ( 2.23%)
+  Jump Register                           8 ( 0.10%)
+  Jump Memory                            10 ( 0.13%)
+  Conditional Jump Immediates          1051 (13.29%)
+  Conditional Jump CX Immediate           0 ( 0.00%)
+  Conditional Jump Register               0 ( 0.00%)
+  Conditional Jump Memory                 0 ( 0.00%)
+  Returns                               160 ( 2.02%)
+
+
+  Relocated Instructions
+  ----------------------
+  Total                                 232 ( 2.93%)
+  addsd                                   2 ( 0.86%)
+  cmp                                    46 (19.83%)
+  comisd                                  2 ( 0.86%)
+  divsd                                   2 ( 0.86%)
+  divss                                   2 ( 0.86%)
+  lea                                   142 (61.21%)
+  mov                                    32 (13.79%)
+  movsd                                   2 ( 0.86%)
+  ucomisd                                 2 ( 0.86%)
+  ```
 
-## FASAN - Frida Address Sanitizer Mode
-Frida mode also supports FASAN. The design of this is actually quite simple and
+* `AFL_FRIDA_STATS_INTERVAL` - The maximum frequency to output statistics
+  information. Stats will be written whenever they are updated if the given
+  interval has elapsed since last time they were written.
+* `AFL_FRIDA_TRACEABLE` - Set the child process to be traceable by any process
+  to aid debugging and overcome the restrictions imposed by YAMA. Supported on
+  Linux only. Permits a non-root user to use `gcore` or similar to collect a
+  core dump of the instrumented target. Note that in order to capture the core
+  dump you must set a sufficient timeout (using `-t`) to avoid `afl-fuzz`
+  killing the process whilst it is being dumped.
+
+## FASAN - FRIDA Address Sanitizer mode
+
+FRIDA mode also supports FASAN. The design of this is actually quite simple and
 very similar to that used when instrumenting applications compiled from source.
 
-### Address Sanitizer Basics
+### Address Sanitizer basics
 
 When Address Sanitizer is used to instrument programs built from source, the
 compiler first adds a dependency (`DT_NEEDED` entry) for the Address Sanitizer
-dynamic shared object (DSO). This shared object contains the main logic for Address
-Sanitizer, including setting and managing up the shadow memory. It also provides
-replacement implementations for a number of functions in standard libraries.
-
-These replacements include things like `malloc` and `free` which allows for those
-allocations to be marked in the shadow memory, but also a number of other fuctions.
-Consider `memcpy` for example, this is instrumented to validate the paramters
-(test the source and destination buffers against the shadow memory. This is much
-easier than instrumenting those standard libraries since, first it would require
-you to re-compile them and secondly it would mean that the instrumentation would
-be applied at a more expensive granular level. Lastly, load-widening (typically
-found in highy optimized code) can also make this instrumentation more difficult.
+dynamic shared object (DSO). This shared object contains the main logic for
+Address Sanitizer, including setting and managing up the shadow memory. It also
+provides replacement implementations for a number of functions in standard
+libraries.
+
+These replacements include things like `malloc` and `free` which allows for
+those allocations to be marked in the shadow memory, but also a number of other
+functions. Consider `memcpy`, for example. This is instrumented to validate the
+parameters (test the source and destination buffers against the shadow memory).
+This is much easier than instrumenting those standard libraries, since first, it
+would require you to re-compile them and secondly it would mean that the
+instrumentation would be applied at a more expensive granular level. Lastly,
+load-widening (typically found in highly optimized code) can also make this
+instrumentation more difficult.
 
 Since the DSO is loaded before all of the standard libraries (in fact it insists
 on being first), the dynamic loader will use it to resolve imports from other
 modules which depend on it.
 
-### FASAN Implementation
+### FASAN implementation
+
+FASAN takes a similar approach. It requires the user to add the Address
+Sanitizer DSO to the `AFL_PRELOAD` environment variable such that it is loaded
+into the target. Again, it must be first in the list. This means that it is not
+necessary to instrument the standard libraries to detect when an application has
+provided an incorrect argument to `memcpy`, for example. This avoids issues with
+load-widening and should also mean a huge improvement in performance.
+
+FASAN then adds instrumentation for any instructions which use memory operands
+and then calls into the `__asan_loadN` and `__asan_storeN` functions provided by
+the DSO to validate memory accesses against the shadow memory.
+
+## Collisions
 
-FASAN takes a similar approach. It requires the user to add the Address Sanitizer
-DSO to the `AFL_PRELOAD` environment variable such that it is loaded into the target.
-Again, it must be first in the list. This means that it is not necessary to
-instrument the standard libraries to detect when an application has provided an
-incorrect argument to `memcpy` for example. This avoids issues with load-widening
-and should also mean a huge improvement in performance.
+FRIDA mode has also introduced some improvements to reduce collisions in the
+map. For details, see [MapDensity.md](MapDensity.md).
 
-FASAN then adds instrumentation for any instrucutions which use memory operands and
-then calls into the `__asan_loadN` and `__asan_storeN` functions provided by the DSO
-to validate memory accesses against the shadow memory.
+## OSX library fuzzing
 
-# Collisions
-FRIDA mode has also introduced some improvements to reduce collisions in the map.
-See [here](MapDensity.md) for details.
+An example of how to fuzz a dynamic library on OSX is included, see
+[test/osx-lib](test/osx-lib). This requires the use of a simple test harness
+executable which will load the library and call a target function within it. The
+dependent library can either be loaded in using `dlopen` and `dlsym` in a
+function marked `__attribute__((constructor()))` or the test harness can be
+linked against it. It is important that the target library is loaded before
+execution of `main`, since this is the point where FRIDA mode is initialized.
+Otherwise, it will not be possible to configure coverage for the test library
+using `AFL_FRIDA_INST_RANGES` or similar.
 
-# OSX Library Fuzzing
-An example of how to fuzz a dynamic library on OSX is included [here](test/osx-lib).
-This requires the use of a simple test harness executable which will load the
-library and call a target function within it. The dependent library can either
-be loaded in using `dlopen` and `dlsym` in a function marked
-`__attribute__((constructor()))` or the test harness can simply be linked
-against it. It is important that the target library is loaded before execution
-of `main`, since this is the point where FRIDA mode is initialized. Otherwise, it
-will not be possible to configure coverage for the test library using
-`AFL_FRIDA_INST_RANGES` or similar.
+## Debugging
 
-# Debugging
-Please refer to the [debugging](#debugging) guide for assistant should you
-encounter problems with FRIDA mode.
+Should you encounter problems with FRIDA mode, refer to
+[DEBUGGING.md](DEBUGGING.md) for assistance.
 
-## TODO
+## To do
 
 The next features to be added are Aarch32 support as well as looking at
-potential performance improvements. The intention is to achieve feature parity with
-QEMU mode in due course. Contributions are welcome, but please get in touch to
-ensure that efforts are deconflicted.
+potential performance improvements. The intention is to achieve feature parity
+with QEMU mode in due course. Contributions are welcome, but please get in touch
+to ensure that efforts are deconflicted.
\ No newline at end of file
diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md
index f6017fad..ad86fdd3 100644
--- a/frida_mode/Scripting.md
+++ b/frida_mode/Scripting.md
@@ -1,25 +1,32 @@
 # Scripting
+
 FRIDA now supports the ability to configure itself using JavaScript. This allows
 the user to make use of the convenience of FRIDA's scripting engine (along with
 it's support for debug symbols and exports) to configure all of the things which
 were traditionally configured using environment variables.
 
-By default FRIDA mode will look for the file `afl.js` in the current working
+By default, FRIDA mode will look for the file `afl.js` in the current working
 directory of the target. Alternatively, a script file can be configured using
 the environment variable `AFL_FRIDA_JS_SCRIPT`.
 
-This script can make use of all of the standard [frida api functions](https://frida.re/docs/javascript-api/), but FRIDA mode adds some additional functions to allow
-you to interact with FRIDA mode itself. These can all be accessed via the global
-`Afl` parameter. e.g. `Afl.print("HELLO WORLD");`,
+This script can make use of all of the standard [frida api
+functions](https://frida.re/docs/javascript-api/), but FRIDA mode adds some
+additional functions to allow you to interact with FRIDA mode itself. These can
+all be accessed via the global `Afl` parameter, e.g., `Afl.print("HELLO
+WORLD");`.
 
 If you encounter a problem with your script, then you should set the environment
 variable `AFL_DEBUG_CHILD=1` to view any diagnostic information.
 
+## Example
 
-# Example
-Most of the time, users will likely be wanting to call the functions which configure an address (e.g. for the entry point, or the persistent address).
+Most of the time, users will likely be wanting to call the functions which
+configure an address (e.g., for the entry point or the persistent address).
 
-The example below uses the API [`DebugSymbol.fromName()`](https://frida.re/docs/javascript-api/#debugsymbol). Another use API is [`Module.getExportByName()`](https://frida.re/docs/javascript-api/#module).
+The example below uses the API
+[`DebugSymbol.fromName()`](https://frida.re/docs/javascript-api/#debugsymbol).
+Another use API is
+[`Module.getExportByName()`](https://frida.re/docs/javascript-api/#module).
 
 ```js
 /* Use Afl.print instead of console.log */
@@ -86,9 +93,9 @@ Afl.done();
 Afl.print("done");
 ```
 
-# Stripped Binaries
+## Stripped binaries
 
-Lastly, if the binary you attempting to fuzz has no symbol information, and no
+Lastly, if the binary you attempting to fuzz has no symbol information and no
 exports, then the following approach can be used.
 
 ```js
@@ -98,11 +105,12 @@ const address = module.base.add(0xdeadface);
 Afl.setPersistentAddress(address);
 ```
 
-# Persisent Hook
+## Persistent hook
+
 A persistent hook can be implemented using a conventional shared object, sample
 source code for a hook suitable for the prototype of `LLVMFuzzerTestOneInput`
-can be found [here](hook/hook.c). This can be configured using code similar to
-the following.
+can be found in [hook/](hook/). This can be configured using code similar to the
+following.
 
 ```js
 const path = Afl.module.path;
@@ -112,7 +120,8 @@ const hook = mod.getExportByName('afl_persistent_hook');
 Afl.setPersistentHook(hook);
 ```
 
-Alternatively, the hook can be provided by using FRIDAs built in support for `CModule`, powered by TinyCC.
+Alternatively, the hook can be provided by using FRIDA's built-in support for
+`CModule`, powered by TinyCC.
 
 ```js
 const cm = new CModule(`
@@ -134,8 +143,10 @@ const cm = new CModule(`
 Afl.setPersistentHook(cm.afl_persistent_hook);
 ```
 
-# Advanced Persistence
+## Advanced persistence
+
 Consider the following target code...
+
 ```c
 
 #include <fcntl.h>
@@ -246,7 +257,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,15 +292,96 @@ 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 parameters `__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 *CANNOT* 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 executable, then a surrogate executable can be used.
+
+## Patching
 
-# Patching
 Consider the [following](test/js/test2.c) test code...
 
 ```c
@@ -302,7 +394,7 @@ Consider the [following](test/js/test2.c) test code...
    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:
-     http://www.apache.org/licenses/LICENSE-2.0
+     https://www.apache.org/licenses/LICENSE-2.0
  */
 
 #include <fcntl.h>
@@ -313,22 +405,22 @@ Consider the [following](test/js/test2.c) test code...
 #include <unistd.h>
 
 const uint32_t crc32_tab[] = {
-	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
 
   ...
 
-	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
 };
 
 uint32_t
 crc32(const void *buf, size_t size)
 {
-	const uint8_t *p = buf;
-	uint32_t crc;
- 	crc = ~0U;
-	while (size--)
-		crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
-	return crc ^ ~0U;
+    const uint8_t *p = buf;
+    uint32_t crc;
+    crc = ~0U;
+    while (size--)
+        crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+    return crc ^ ~0U;
 }
 
 /*
@@ -419,9 +511,9 @@ int main(int argc, char **argv) {
 ```
 
 There are a couple of obstacles with our target application. Unlike when fuzzing
-source code, though, we can't simply edit it and recompile it. The following
+source code, though, we can't just edit it and recompile it. The following
 script shows how we can use the normal functionality of FRIDA to modify any
-troublesome behaviour.
+troublesome behavior.
 
 ```js
 Afl.print('******************');
@@ -460,8 +552,10 @@ Afl.done();
 Afl.print("done");
 ```
 
-# Advanced Patching
+## Advanced patching
+
 Consider the following code fragment...
+
 ```c
 extern void some_boring_bug2(char c);
 
@@ -488,7 +582,7 @@ void LLVMFuzzerTestOneInput(char *buf, int len) {
 }
 ```
 
-Rather than using FRIDAs `Interceptor.replace` or `Interceptor.attach` APIs, it
+Rather than using FRIDA's `Interceptor.replace` or `Interceptor.attach` APIs, it
 is possible to apply much more fine grained modification to the target
 application by means of using the Stalker APIs.
 
@@ -572,39 +666,43 @@ Afl.setStalkerCallback(cm.js_stalker_callback)
 Afl.setStdErr("/tmp/stderr.txt");
 ```
 
-Note that you will more likely want to find the
-patch address by using:
+Note that you will more likely want to find the patch address by using:
 
 ```js
 const module = Process.getModuleByName('target.exe');
 /* Hardcoded offset within the target image */
 const address = module.base.add(0xdeadface);
 ```
+
 OR
+
 ```
 const address = DebugSymbol.fromName("my_function").address.add(0xdeadface);
 ```
+
 OR
+
 ```
 const address = Module.getExportByName(null, "my_function").add(0xdeadface);
 ```
 
 The function `js_stalker_callback` should return `TRUE` if the original
-instruction should be emitted in the instrumented code, or `FALSE` otherwise.
-In the example above, we can see it is replaced with a `NOP`.
+instruction should be emitted in the instrumented code or `FALSE` otherwise. In
+the example above, we can see it is replaced with a `NOP`.
 
 Lastly, note that the same callback will be called when compiling instrumented
 code both in the child of the forkserver (as it is executed) and also in the
-parent of the forserver (when prefetching is enabled) so that it can be
+parent of the forkserver (when prefetching is enabled) so that it can be
 inherited by the next forked child. It is **VERY** important that the same
-instructions be generated in both the parent and the child, or if prefetching is
+instructions be generated in both the parent and the child or if prefetching is
 disabled that the same instructions are generated every time the block is
 compiled. Failure to do so will likely lead to bugs which are incredibly
 difficult to diagnose. The code above only prints the instructions when running
 in the parent process (the one provided by `Process.id` when the JS script is
 executed).
 
-# OSX
+## OSX
+
 Note that the JavaScript debug symbol api for OSX makes use of the
 `CoreSymbolication` APIs and as such the `CoreFoundation` module must be loaded
 into the target to make use of it. This can be done by setting:
@@ -614,47 +712,38 @@ AFL_PRELOAD=/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
 ```
 
 It should be noted that `CoreSymbolication` API may take a while to initialize
-and build its caches. For this reason, it may be nescessary to also increase the
+and build its caches. For this reason, it may be necessary to also increase the
 value of the `-t` flag passed to `afl-fuzz`.
 
-# API
+## 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 +751,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 +801,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);
   }
-
 }
-
-```
+```
\ No newline at end of file
diff --git a/frida_mode/frida.map b/frida_mode/frida.map
index 0fc48aa6..5276db91 100644
--- a/frida_mode/frida.map
+++ b/frida_mode/frida.map
@@ -8,6 +8,7 @@
     js_api_add_include_range;
     js_api_done;
     js_api_error;
+    js_api_set_backpatch_disable;
     js_api_set_debug_maps;
     js_api_set_entrypoint;
     js_api_set_instrument_coverage_file;
@@ -19,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;
@@ -28,11 +30,13 @@
     js_api_set_prefetch_disable;
     js_api_set_seccomp_file;
     js_api_set_stalker_callback;
+    js_api_set_stalker_adjacent_blocks;
     js_api_set_stalker_ic_entries;
     js_api_set_stats_file;
     js_api_set_stats_interval;
     js_api_set_stderr;
     js_api_set_stdout;
+    js_api_set_traceable;
 
   local:
     *;
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/entry.h b/frida_mode/include/entry.h
index 3f0a4ecc..edc41467 100644
--- a/frida_mode/include/entry.h
+++ b/frida_mode/include/entry.h
@@ -4,6 +4,7 @@
 #include "frida-gumjs.h"
 
 extern guint64  entry_point;
+extern gboolean traceable;
 extern gboolean entry_compiled;
 extern gboolean entry_run;
 
@@ -15,5 +16,7 @@ void entry_start(void);
 
 void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
 
+void entry_on_fork(void);
+
 #endif
 
diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h
index 909b2a2c..a5d52616 100644
--- a/frida_mode/include/instrument.h
+++ b/frida_mode/include/instrument.h
@@ -29,13 +29,15 @@ GumStalkerTransformer *instrument_get_transformer(void);
 /* Functions to be implemented by the different architectures */
 gboolean instrument_is_coverage_optimize_supported(void);
 
+void instrument_coverage_optimize_init(void);
 void instrument_coverage_optimize(const cs_insn *   instr,
                                   GumStalkerOutput *output);
 
 void     instrument_debug_config(void);
 void     instrument_debug_init(void);
 void     instrument_debug_start(uint64_t address, GumStalkerOutput *output);
-void     instrument_debug_instruction(uint64_t address, uint16_t size);
+void     instrument_debug_instruction(uint64_t address, uint16_t size,
+                                      GumStalkerOutput *output);
 void     instrument_debug_end(GumStalkerOutput *output);
 void     instrument_flush(GumStalkerOutput *output);
 gpointer instrument_cur(GumStalkerOutput *output);
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/include/ranges.h b/frida_mode/include/ranges.h
index 0220a59d..3bd9eaa6 100644
--- a/frida_mode/include/ranges.h
+++ b/frida_mode/include/ranges.h
@@ -10,6 +10,8 @@ extern gboolean ranges_inst_jit;
 void ranges_config(void);
 void ranges_init(void);
 
+void ranges_print_debug_maps(void);
+
 gboolean range_is_excluded(GumAddress address);
 
 void ranges_exclude();
diff --git a/frida_mode/include/seccomp.h b/frida_mode/include/seccomp.h
index 2c037ff7..7e8a7d25 100644
--- a/frida_mode/include/seccomp.h
+++ b/frida_mode/include/seccomp.h
@@ -1,15 +1,95 @@
 #ifndef _SECCOMP_H
 #define _SECCOMP_H
 
-#include <linux/seccomp.h>
+#ifndef __APPLE__
 
-#include "frida-gumjs.h"
+  #include <stdint.h>
+  #include <linux/filter.h>
 
-#define SECCOMP_SOCKET_SEND_FD 0x1D3
-#define SECCOMP_SOCKET_RECV_FD 0x1D4
+  #include "frida-gumjs.h"
 
-#define SECCOMP_OUTPUT_FILE_FD 0x1D5
-#define SECCOMP_PARENT_EVENT_FD 0x1D6
+  /******************************************************************************/
+  #define PR_SET_NO_NEW_PRIVS 38
+
+  #define SECCOMP_SET_MODE_STRICT 0
+  #define SECCOMP_SET_MODE_FILTER 1
+  #define SECCOMP_GET_ACTION_AVAIL 2
+  #define SECCOMP_GET_NOTIF_SIZES 3
+
+  #define SECCOMP_IOC_MAGIC '!'
+  #define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr)
+  #define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type)
+  #define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type)
+  #define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type)
+
+  /* Flags for seccomp notification fd ioctl. */
+  #define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif)
+  #define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, struct seccomp_notif_resp)
+  #define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOW(2, __u64)
+
+  #define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
+  #define SECCOMP_RET_ALLOW 0x7fff0000U
+  #define SECCOMP_RET_USER_NOTIF 0x7fc00000U
+
+  #define SYS_seccomp __NR_seccomp
+  #ifndef __NR_seccomp
+    #if defined(__arm__)
+      #define __NR_seccomp 383
+    #elif defined(__aarch64__)
+      #define __NR_seccomp 277
+    #elif defined(__x86_64__)
+      #define __NR_seccomp 317
+    #elif defined(__i386__)
+      #define __NR_seccomp 354
+    #else
+      #pragma error "Unsupported architecture"
+    #endif
+  #endif
+
+  #define SECCOMP_USER_NOTIF_FLAG_CONTINUE (1UL << 0)
+
+struct seccomp_notif_resp {
+
+  __u64 id;
+  __s64 val;
+  __s32 error;
+  __u32 flags;
+
+};
+
+struct seccomp_data {
+
+  int   nr;
+  __u32 arch;
+  __u64 instruction_pointer;
+  __u64 args[6];
+
+};
+
+struct seccomp_notif {
+
+  __u64               id;
+  __u32               pid;
+  __u32               flags;
+  struct seccomp_data data;
+
+};
+
+struct seccomp_notif_sizes {
+
+  __u16 seccomp_notif;
+  __u16 seccomp_notif_resp;
+  __u16 seccomp_data;
+
+};
+
+  /******************************************************************************/
+
+  #define SECCOMP_SOCKET_SEND_FD 0x1D3
+  #define SECCOMP_SOCKET_RECV_FD 0x1D4
+
+  #define SECCOMP_OUTPUT_FILE_FD 0x1D5
+  #define SECCOMP_PARENT_EVENT_FD 0x1D6
 
 enum {
 
@@ -319,23 +399,19 @@ enum {
 
 };
 
-extern char *seccomp_filename;
-
 typedef void (*seccomp_child_func_t)(int event_fd, void *ctx);
 
 typedef void (*seccomp_filter_callback_t)(struct seccomp_notif *     req,
                                           struct seccomp_notif_resp *resp,
                                           GumReturnAddressArray *    frames);
 
-void seccomp_config(void);
-void seccomp_init(void);
-void seccomp_on_fork(void);
-void seccomp_print(char *format, ...);
-
 void seccomp_atomic_set(volatile bool *ptr, bool val);
 bool seccomp_atomic_try_set(volatile bool *ptr, bool val);
 void seccomp_atomic_wait(volatile bool *ptr, bool val);
 
+void seccomp_callback_parent(void);
+void seccomp_callback_initialize(void);
+
 void seccomp_child_run(seccomp_child_func_t child_func, void *ctx, pid_t *child,
                        int *event_fd);
 void seccomp_child_wait(int event_fd);
@@ -349,6 +425,8 @@ int  seccomp_filter_install(pid_t child);
 void seccomp_filter_child_install(void);
 void seccomp_filter_run(int fd, seccomp_filter_callback_t callback);
 
+void seccomp_print(char *format, ...);
+
 void seccomp_socket_create(int *sock);
 void seccomp_socket_send(int sockfd, int fd);
 int  seccomp_socket_recv(int sockfd);
@@ -356,4 +434,11 @@ int  seccomp_socket_recv(int sockfd);
 char *seccomp_syscall_lookup(int id);
 
 #endif
+extern char *seccomp_filename;
+
+void seccomp_config(void);
+void seccomp_init(void);
+void seccomp_on_fork(void);
+
+#endif
 
diff --git a/frida_mode/include/stalker.h b/frida_mode/include/stalker.h
index 955f3913..666787e9 100644
--- a/frida_mode/include/stalker.h
+++ b/frida_mode/include/stalker.h
@@ -3,7 +3,9 @@
 
 #include "frida-gumjs.h"
 
-extern guint stalker_ic_entries;
+extern guint    stalker_ic_entries;
+extern gboolean backpatch_enable;
+extern guint    stalker_adjacent_blocks;
 
 void        stalker_config(void);
 void        stalker_init(void);
diff --git a/frida_mode/include/util.h b/frida_mode/include/util.h
index 525e9d40..4dc05f20 100644
--- a/frida_mode/include/util.h
+++ b/frida_mode/include/util.h
@@ -3,12 +3,39 @@
 
 #include "frida-gumjs.h"
 
+#include "debug.h"
+
 #define UNUSED_PARAMETER(x) (void)(x)
 #define IGNORED_RETURN(x) (void)!(x)
 
-guint64 util_read_address(char *key);
+guint64  util_read_address(char *key, guint64 default_value);
+guint64  util_read_num(char *key, guint64 default_value);
+gboolean util_output_enabled(void);
+gsize    util_rotate(gsize val, gsize shift, gsize size);
+gsize    util_log2(gsize val);
+
+#define FOKF(x...)                         \
+  do {                                     \
+                                           \
+    if (!util_output_enabled()) { break; } \
+                                           \
+    OKF(x);                                \
+                                           \
+  } while (0)
+
+#define FWARNF(x...) \
+  do {               \
+                     \
+    WARNF(x);        \
+                     \
+  } while (0)
 
-guint64 util_read_num(char *key);
+#define FFATAL(x...) \
+  do {               \
+                     \
+    FATAL(x);        \
+                     \
+  } while (0)
 
 #endif
 
diff --git a/frida_mode/many-linux/Dockerfile b/frida_mode/many-linux/Dockerfile
index 170f0757..6d077dad 100644
--- a/frida_mode/many-linux/Dockerfile
+++ b/frida_mode/many-linux/Dockerfile
@@ -1,10 +1,6 @@
 FROM fridadotre/manylinux-x86_64
 
-COPY realpath /bin/realpath
-RUN chmod +x /bin/realpath
-
 RUN yum -y install xz
-RUN yum -y install vim-common
 
 WORKDIR /AFLplusplus
 ENV CFLAGS="\
diff --git a/frida_mode/many-linux/GNUmakefile b/frida_mode/many-linux/GNUmakefile
index 03b619f6..9992d4fe 100644
--- a/frida_mode/many-linux/GNUmakefile
+++ b/frida_mode/many-linux/GNUmakefile
@@ -2,17 +2,22 @@ PWD:=$(shell pwd)/
 ROOT:=$(PWD)../../
 BUILD_DIR:=$(PWD)build/
 
-.PHONY: all clean shell
+.PHONY: all build docker clean shell
 
-all:
-	docker build --tag many-afl-frida .
+all: docker
 	docker run --rm \
 		-v $(ROOT):/AFLplusplus \
 		many-afl-frida \
 		make -C /AFLplusplus/frida_mode clean all
 
-$(BUILD_DIR):
-	mkdir -p $@
+build:
+	docker run --rm \
+		-v $(ROOT):/AFLplusplus \
+		many-afl-frida \
+		make -C /AFLplusplus/frida_mode
+
+docker:
+	docker build --tag many-afl-frida .
 
 clean:
 	docker images --filter 'dangling=true' -q --no-trunc | xargs -L1 docker rmi --force
diff --git a/frida_mode/many-linux/realpath b/frida_mode/many-linux/realpath
deleted file mode 100755
index 1fdc49a7..00000000
--- a/frida_mode/many-linux/realpath
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-readlink -f -- "$@"
diff --git a/frida_mode/src/asan/asan.c b/frida_mode/src/asan/asan.c
index b2e763ca..884bec53 100644
--- a/frida_mode/src/asan/asan.c
+++ b/frida_mode/src/asan/asan.c
@@ -1,8 +1,8 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "asan.h"
+#include "ranges.h"
+#include "util.h"
 
 static gboolean asan_enabled = FALSE;
 gboolean        asan_initialized = FALSE;
@@ -11,12 +11,12 @@ void asan_config(void) {
 
   if (getenv("AFL_USE_FASAN") != NULL) {
 
-    OKF("Frida ASAN mode enabled");
+    FOKF("Frida ASAN mode enabled");
     asan_enabled = TRUE;
 
   } else {
 
-    OKF("Frida ASAN mode disabled");
+    FOKF("Frida ASAN mode disabled");
 
   }
 
@@ -33,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_arm32.c b/frida_mode/src/asan/asan_arm32.c
index f5fa4713..21400881 100644
--- a/frida_mode/src/asan/asan_arm32.c
+++ b/frida_mode/src/asan/asan_arm32.c
@@ -1,7 +1,5 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "asan.h"
 #include "util.h"
 
@@ -12,7 +10,7 @@ void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
   UNUSED_PARAMETER(iterator);
   if (asan_initialized) {
 
-    FATAL("ASAN mode not supported on this architecture");
+    FFATAL("ASAN mode not supported on this architecture");
 
   }
 
@@ -20,7 +18,7 @@ void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
 
 void asan_arch_init(void) {
 
-  FATAL("ASAN mode not supported on this architecture");
+  FFATAL("ASAN mode not supported on this architecture");
 
 }
 
diff --git a/frida_mode/src/asan/asan_arm64.c b/frida_mode/src/asan/asan_arm64.c
index 65524e03..b2adfa52 100644
--- a/frida_mode/src/asan/asan_arm64.c
+++ b/frida_mode/src/asan/asan_arm64.c
@@ -1,8 +1,6 @@
 #include <dlfcn.h>
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "asan.h"
 #include "ctx.h"
 #include "util.h"
@@ -86,10 +84,12 @@ void asan_arch_init(void) {
   asan_storeN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_storeN");
   if (asan_loadN == NULL || asan_storeN == NULL) {
 
-    FATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
+    FFATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
 
   }
 
+  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 5c12669f..a287ea34 100644
--- a/frida_mode/src/asan/asan_x64.c
+++ b/frida_mode/src/asan/asan_x64.c
@@ -1,8 +1,6 @@
 #include <dlfcn.h>
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "asan.h"
 #include "ctx.h"
 #include "util.h"
@@ -83,10 +81,12 @@ void asan_arch_init(void) {
   asan_storeN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_storeN");
   if (asan_loadN == NULL || asan_storeN == NULL) {
 
-    FATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
+    FFATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
 
   }
 
+  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 6d2f9e2b..331d026b 100644
--- a/frida_mode/src/asan/asan_x86.c
+++ b/frida_mode/src/asan/asan_x86.c
@@ -1,8 +1,6 @@
 #include <dlfcn.h>
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "asan.h"
 #include "ctx.h"
 #include "util.h"
@@ -83,10 +81,12 @@ void asan_arch_init(void) {
   asan_storeN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_storeN");
   if (asan_loadN == NULL || asan_storeN == NULL) {
 
-    FATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
+    FFATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
 
   }
 
+  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 ae3116eb..443baa1d 100644
--- a/frida_mode/src/cmplog/cmplog.c
+++ b/frida_mode/src/cmplog/cmplog.c
@@ -7,8 +7,6 @@
 
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "util.h"
 
 #define DEFAULT_MMAP_MIN_ADDR (32UL << 10)
@@ -35,14 +33,28 @@ 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;
+
+  }
 
 }
 
 static void cmplog_get_ranges(void) {
 
-  OKF("CMPLOG - Collecting ranges");
+  FOKF("CMPLOG - Collecting ranges");
 
   cmplog_ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), 100);
   gum_process_enumerate_ranges(GUM_PAGE_READ, cmplog_range, cmplog_ranges);
@@ -56,7 +68,7 @@ void cmplog_config(void) {
 
 void cmplog_init(void) {
 
-  OKF("CMPLOG - Enabled [%c]", __afl_cmp_map == NULL ? ' ' : 'X');
+  FOKF("CMPLOG - Enabled [%c]", __afl_cmp_map == NULL ? ' ' : 'X');
 
   if (__afl_cmp_map == NULL) { return; }
 
@@ -65,9 +77,9 @@ void cmplog_init(void) {
   for (guint i = 0; i < cmplog_ranges->len; i++) {
 
     GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i);
-    OKF("CMPLOG Range - %3u: 0x%016" G_GINT64_MODIFIER
-        "X - 0x%016" G_GINT64_MODIFIER "X",
-        i, range->base_address, range->base_address + range->size);
+    FOKF("CMPLOG Range - %3u: 0x%016" G_GINT64_MODIFIER
+         "X - 0x%016" G_GINT64_MODIFIER "X",
+         i, range->base_address, range->base_address + range->size);
 
   }
 
@@ -78,14 +90,14 @@ void cmplog_init(void) {
   hash_yes = g_hash_table_new(g_direct_hash, g_direct_equal);
   if (hash_yes == NULL) {
 
-    FATAL("Failed to g_hash_table_new, errno: %d", errno);
+    FFATAL("Failed to g_hash_table_new, errno: %d", errno);
 
   }
 
   hash_no = g_hash_table_new(g_direct_hash, g_direct_equal);
   if (hash_no == NULL) {
 
-    FATAL("Failed to g_hash_table_new, errno: %d", errno);
+    FFATAL("Failed to g_hash_table_new, errno: %d", errno);
 
   }
 
@@ -117,7 +129,7 @@ gboolean cmplog_test_addr(guint64 addr, size_t size) {
 
     if (!g_hash_table_add(hash_no, GSIZE_TO_POINTER(addr))) {
 
-      FATAL("Failed - g_hash_table_add");
+      FFATAL("Failed - g_hash_table_add");
 
     }
 
@@ -127,7 +139,7 @@ gboolean cmplog_test_addr(guint64 addr, size_t size) {
 
     if (!g_hash_table_add(hash_yes, GSIZE_TO_POINTER(addr))) {
 
-      FATAL("Failed - g_hash_table_add");
+      FFATAL("Failed - g_hash_table_add");
 
     }
 
@@ -139,7 +151,7 @@ gboolean cmplog_test_addr(guint64 addr, size_t size) {
 
 gboolean cmplog_is_readable(guint64 addr, size_t size) {
 
-  if (cmplog_ranges == NULL) FATAL("CMPLOG not initialized");
+  if (cmplog_ranges == NULL) FFATAL("CMPLOG not initialized");
 
   /*
    * The Linux kernel prevents mmap from allocating from the very bottom of the
diff --git a/frida_mode/src/cmplog/cmplog_arm32.c b/frida_mode/src/cmplog/cmplog_arm32.c
index ac703408..106baa52 100644
--- a/frida_mode/src/cmplog/cmplog_arm32.c
+++ b/frida_mode/src/cmplog/cmplog_arm32.c
@@ -1,7 +1,5 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "frida_cmplog.h"
 #include "util.h"
 
@@ -11,7 +9,7 @@ void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
   UNUSED_PARAMETER(instr);
   UNUSED_PARAMETER(iterator);
   if (__afl_cmp_map == NULL) { return; }
-  FATAL("CMPLOG mode not supported on this architecture");
+  FFATAL("CMPLOG mode not supported on this architecture");
 
 }
 
diff --git a/frida_mode/src/cmplog/cmplog_arm64.c b/frida_mode/src/cmplog/cmplog_arm64.c
index dd97f38d..515a6256 100644
--- a/frida_mode/src/cmplog/cmplog_arm64.c
+++ b/frida_mode/src/cmplog/cmplog_arm64.c
@@ -5,6 +5,7 @@
 
 #include "ctx.h"
 #include "frida_cmplog.h"
+#include "instrument.h"
 #include "util.h"
 
 #if defined(__aarch64__)
@@ -66,7 +67,7 @@ static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
       *val = *((guint64 *)GSIZE_TO_POINTER(address));
       return TRUE;
     default:
-      FATAL("Invalid operand size: %d\n", size);
+      FFATAL("Invalid operand size: %d\n", size);
 
   }
 
@@ -88,7 +89,7 @@ static gboolean cmplog_get_operand_value(GumCpuContext *context,
     case ARM64_OP_MEM:
       return cmplog_read_mem(context, ctx->size, &ctx->mem, val);
     default:
-      FATAL("Invalid operand type: %d\n", ctx->type);
+      FFATAL("Invalid operand type: %d\n", ctx->type);
 
   }
 
@@ -104,30 +105,45 @@ static void cmplog_call_callout(GumCpuContext *context, gpointer user_data) {
   gsize x0 = ctx_read_reg(context, ARM64_REG_X0);
   gsize x1 = ctx_read_reg(context, ARM64_REG_X1);
 
-  if (((G_MAXULONG - x0) < 32) || ((G_MAXULONG - x1) < 32)) return;
+  if (((G_MAXULONG - x0) < 31) || ((G_MAXULONG - x1) < 31)) return;
 
-  if (!cmplog_is_readable(x0, 32) || !cmplog_is_readable(x1, 32)) return;
+  if (!cmplog_is_readable(x0, 31) || !cmplog_is_readable(x1, 31)) return;
 
   void *ptr1 = GSIZE_TO_POINTER(x0);
   void *ptr2 = GSIZE_TO_POINTER(x1);
 
-  uintptr_t k = address;
+  guint64 k = instrument_get_offset_hash(GUM_ADDRESS(address));
 
-  k = (k >> 4) ^ (k << 8);
-  k &= CMP_MAP_W - 1;
+  if (__afl_cmp_map->headers[k].type != CMP_TYPE_RTN) {
+
+    __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
+    __afl_cmp_map->headers[k].hits = 0;
+
+  }
 
-  __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
+  u32 hits = 0;
+
+  if (__afl_cmp_map->headers[k].hits == 0) {
+
+    __afl_cmp_map->headers[k].shape = 30;
+
+  } else {
+
+    hits = __afl_cmp_map->headers[k].hits;
+
+  }
 
-  u32 hits = __afl_cmp_map->headers[k].hits;
   __afl_cmp_map->headers[k].hits = hits + 1;
 
-  __afl_cmp_map->headers[k].shape = 31;
+  __afl_cmp_map->headers[k].shape = 30;
 
   hits &= CMP_MAP_RTN_H - 1;
+  ((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0_len = 31;
+  ((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1_len = 31;
   gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, ptr1,
-             32);
+             31);
   gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2,
-             32);
+             31);
 
 }
 
@@ -147,7 +163,7 @@ static void cmplog_instrument_put_operand(cmplog_ctx_t *ctx,
       gum_memcpy(&ctx->mem, &operand->mem, sizeof(arm64_op_mem));
       break;
     default:
-      FATAL("Invalid operand type: %d\n", operand->type);
+      FFATAL("Invalid operand type: %d\n", operand->type);
 
   }
 
@@ -193,12 +209,23 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
   k = (k >> 4) ^ (k << 8);
   k &= CMP_MAP_W - 1;
 
-  __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+  if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS)
+    __afl_cmp_map->headers[k].hits = 0;
 
-  u32 hits = __afl_cmp_map->headers[k].hits;
-  __afl_cmp_map->headers[k].hits = hits + 1;
+  u32 hits = 0;
+
+  if (__afl_cmp_map->headers[k].hits == 0) {
+
+    __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+    __afl_cmp_map->headers[k].shape = (size - 1);
+
+  } else {
 
-  __afl_cmp_map->headers[k].shape = (size - 1);
+    hits = __afl_cmp_map->headers[k].hits;
+
+  }
+
+  __afl_cmp_map->headers[k].hits = hits + 1;
 
   hits &= CMP_MAP_H - 1;
   __afl_cmp_map->log[k][hits].v0 = operand1;
diff --git a/frida_mode/src/cmplog/cmplog_x64.c b/frida_mode/src/cmplog/cmplog_x64.c
index 0d18767a..7d515336 100644
--- a/frida_mode/src/cmplog/cmplog_x64.c
+++ b/frida_mode/src/cmplog/cmplog_x64.c
@@ -5,6 +5,7 @@
 
 #include "ctx.h"
 #include "frida_cmplog.h"
+#include "instrument.h"
 #include "util.h"
 
 #if defined(__x86_64__)
@@ -61,7 +62,7 @@ static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
       *val = *((guint64 *)GSIZE_TO_POINTER(address));
       return TRUE;
     default:
-      FATAL("Invalid operand size: %d\n", size);
+      FFATAL("Invalid operand size: %d\n", size);
 
   }
 
@@ -83,7 +84,7 @@ static gboolean cmplog_get_operand_value(GumCpuContext *context,
     case X86_OP_MEM:
       return cmplog_read_mem(context, ctx->size, &ctx->mem, val);
     default:
-      FATAL("Invalid operand type: %d\n", ctx->type);
+      FFATAL("Invalid operand type: %d\n", ctx->type);
 
   }
 
@@ -99,30 +100,43 @@ static void cmplog_call_callout(GumCpuContext *context, gpointer user_data) {
   gsize rdi = ctx_read_reg(context, X86_REG_RDI);
   gsize rsi = ctx_read_reg(context, X86_REG_RSI);
 
-  if (((G_MAXULONG - rdi) < 32) || ((G_MAXULONG - rsi) < 32)) return;
+  if (((G_MAXULONG - rdi) < 31) || ((G_MAXULONG - rsi) < 31)) return;
 
-  if (!cmplog_is_readable(rdi, 32) || !cmplog_is_readable(rsi, 32)) return;
+  if (!cmplog_is_readable(rdi, 31) || !cmplog_is_readable(rsi, 31)) return;
 
   void *ptr1 = GSIZE_TO_POINTER(rdi);
   void *ptr2 = GSIZE_TO_POINTER(rsi);
 
-  uintptr_t k = address;
+  guint64 k = instrument_get_offset_hash(GUM_ADDRESS(address));
 
-  k = (k >> 4) ^ (k << 8);
-  k &= CMP_MAP_W - 1;
+  if (__afl_cmp_map->headers[k].type != CMP_TYPE_RTN) {
 
-  __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
+    __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
+    __afl_cmp_map->headers[k].hits = 0;
 
-  u32 hits = __afl_cmp_map->headers[k].hits;
-  __afl_cmp_map->headers[k].hits = hits + 1;
+  }
+
+  u32 hits = 0;
+
+  if (__afl_cmp_map->headers[k].hits == 0) {
+
+    __afl_cmp_map->headers[k].shape = 30;
+
+  } else {
+
+    hits = __afl_cmp_map->headers[k].hits;
+
+  }
 
-  __afl_cmp_map->headers[k].shape = 31;
+  __afl_cmp_map->headers[k].hits = hits + 1;
 
   hits &= CMP_MAP_RTN_H - 1;
+  ((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0_len = 31;
+  ((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1_len = 31;
   gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, ptr1,
-             32);
+             31);
   gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2,
-             32);
+             31);
 
 }
 
@@ -143,7 +157,7 @@ static void cmplog_instrument_put_operand(cmplog_ctx_t *ctx,
       gum_memcpy(&ctx->mem, &operand->mem, sizeof(x86_op_mem));
       break;
     default:
-      FATAL("Invalid operand type: %d\n", operand->type);
+      FFATAL("Invalid operand type: %d\n", operand->type);
 
   }
 
@@ -179,13 +193,23 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
   k = (k >> 4) ^ (k << 8);
   k &= CMP_MAP_W - 7;
 
-  __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+  if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS)
+    __afl_cmp_map->headers[k].hits = 0;
 
-  u32 hits = __afl_cmp_map->headers[k].hits;
-  __afl_cmp_map->headers[k].hits = hits + 1;
+  u32 hits = 0;
+
+  if (__afl_cmp_map->headers[k].hits == 0) {
+
+    __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+    __afl_cmp_map->headers[k].shape = (size - 1);
 
-  __afl_cmp_map->headers[k].shape = (size - 1);
+  } else {
 
+    hits = __afl_cmp_map->headers[k].hits;
+
+  }
+
+  __afl_cmp_map->headers[k].hits = hits + 1;
   hits &= CMP_MAP_H - 1;
   __afl_cmp_map->log[k][hits].v0 = operand1;
   __afl_cmp_map->log[k][hits].v1 = operand2;
diff --git a/frida_mode/src/cmplog/cmplog_x86.c b/frida_mode/src/cmplog/cmplog_x86.c
index dd666c34..4a747417 100644
--- a/frida_mode/src/cmplog/cmplog_x86.c
+++ b/frida_mode/src/cmplog/cmplog_x86.c
@@ -5,6 +5,7 @@
 
 #include "ctx.h"
 #include "frida_cmplog.h"
+#include "instrument.h"
 #include "util.h"
 
 #if defined(__i386__)
@@ -58,7 +59,7 @@ static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
       *val = *((guint32 *)GSIZE_TO_POINTER(address));
       return TRUE;
     default:
-      FATAL("Invalid operand size: %d\n", size);
+      FFATAL("Invalid operand size: %d\n", size);
 
   }
 
@@ -80,7 +81,7 @@ static gboolean cmplog_get_operand_value(GumCpuContext *context,
     case X86_OP_MEM:
       return cmplog_read_mem(context, ctx->size, &ctx->mem, val);
     default:
-      FATAL("Invalid operand type: %d\n", ctx->type);
+      FFATAL("Invalid operand type: %d\n", ctx->type);
 
   }
 
@@ -104,30 +105,43 @@ static void cmplog_call_callout(GumCpuContext *context, gpointer user_data) {
   gsize arg1 = esp[0];
   gsize arg2 = esp[1];
 
-  if (((G_MAXULONG - arg1) < 32) || ((G_MAXULONG - arg2) < 32)) return;
+  if (((G_MAXULONG - arg1) < 31) || ((G_MAXULONG - arg2) < 31)) return;
 
-  if (!cmplog_is_readable(arg1, 32) || !cmplog_is_readable(arg2, 32)) return;
+  if (!cmplog_is_readable(arg1, 31) || !cmplog_is_readable(arg2, 31)) return;
 
   void *ptr1 = GSIZE_TO_POINTER(arg1);
   void *ptr2 = GSIZE_TO_POINTER(arg2);
 
-  uintptr_t k = address;
+  guint64 k = instrument_get_offset_hash(GUM_ADDRESS(address));
 
-  k = (k >> 4) ^ (k << 8);
-  k &= CMP_MAP_W - 1;
+  if (__afl_cmp_map->headers[k].type != CMP_TYPE_RTN) {
 
-  __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
+    __afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
+    __afl_cmp_map->headers[k].hits = 0;
 
-  u32 hits = __afl_cmp_map->headers[k].hits;
-  __afl_cmp_map->headers[k].hits = hits + 1;
+  }
+
+  u32 hits = 0;
+
+  if (__afl_cmp_map->headers[k].hits == 0) {
+
+    __afl_cmp_map->headers[k].shape = 30;
+
+  } else {
+
+    hits = __afl_cmp_map->headers[k].hits;
+
+  }
 
-  __afl_cmp_map->headers[k].shape = 31;
+  __afl_cmp_map->headers[k].hits = hits + 1;
 
   hits &= CMP_MAP_RTN_H - 1;
+  ((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0_len = 31;
+  ((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1_len = 31;
   gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, ptr1,
-             32);
+             31);
   gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2,
-             32);
+             31);
 
 }
 
@@ -148,7 +162,7 @@ static void cmplog_instrument_put_operand(cmplog_ctx_t *ctx,
       gum_memcpy(&ctx->mem, &operand->mem, sizeof(x86_op_mem));
       break;
     default:
-      FATAL("Invalid operand type: %d\n", operand->type);
+      FFATAL("Invalid operand type: %d\n", operand->type);
 
   }
 
@@ -184,12 +198,23 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
   k = (k >> 4) ^ (k << 8);
   k &= CMP_MAP_W - 1;
 
-  __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+  if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS)
+    __afl_cmp_map->headers[k].hits = 0;
 
-  u32 hits = __afl_cmp_map->headers[k].hits;
-  __afl_cmp_map->headers[k].hits = hits + 1;
+  u32 hits = 0;
+
+  if (__afl_cmp_map->headers[k].hits == 0) {
+
+    __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+    __afl_cmp_map->headers[k].shape = (size - 1);
 
-  __afl_cmp_map->headers[k].shape = (size - 1);
+  } else {
+
+    hits = __afl_cmp_map->headers[k].hits;
+
+  }
+
+  __afl_cmp_map->headers[k].hits = hits + 1;
 
   hits &= CMP_MAP_H - 1;
   __afl_cmp_map->log[k][hits].v0 = operand1;
@@ -203,7 +228,7 @@ static void cmplog_cmp_sub_callout(GumCpuContext *context, gpointer user_data) {
   gsize              operand1;
   gsize              operand2;
 
-  if (ctx->operand1.size != ctx->operand2.size) FATAL("Operand size mismatch");
+  if (ctx->operand1.size != ctx->operand2.size) FFATAL("Operand size mismatch");
 
   if (!cmplog_get_operand_value(context, &ctx->operand1, &operand1)) { return; }
   if (!cmplog_get_operand_value(context, &ctx->operand2, &operand2)) { return; }
diff --git a/frida_mode/src/ctx/ctx_arm32.c b/frida_mode/src/ctx/ctx_arm32.c
index 9fc70fb4..28fc706b 100644
--- a/frida_mode/src/ctx/ctx_arm32.c
+++ b/frida_mode/src/ctx/ctx_arm32.c
@@ -1,14 +1,13 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "ctx.h"
+#include "util.h"
 
 #if defined(__arm__)
 
 gsize ctx_read_reg(GumArmCpuContext *ctx, arm_reg reg) {
 
-  FATAL("ctx_read_reg unimplemented for this architecture");
+  FFATAL("ctx_read_reg unimplemented for this architecture");
 
 }
 
diff --git a/frida_mode/src/ctx/ctx_arm64.c b/frida_mode/src/ctx/ctx_arm64.c
index a735401b..63b6cf09 100644
--- a/frida_mode/src/ctx/ctx_arm64.c
+++ b/frida_mode/src/ctx/ctx_arm64.c
@@ -1,8 +1,7 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "ctx.h"
+#include "util.h"
 
 #if defined(__aarch64__)
 
@@ -174,7 +173,7 @@ gsize ctx_read_reg(GumArm64CpuContext *ctx, arm64_reg reg) {
       ARM64_REG_64(ARM64_REG_SP, ctx->sp)
 
     default:
-      FATAL("Failed to read register: %d", reg);
+      FFATAL("Failed to read register: %d", reg);
       return 0;
 
   }
@@ -206,7 +205,7 @@ size_t ctx_get_size(const cs_insn *instr, cs_arm64_op *operand) {
   }
 
   mnemonic_len = strlen(instr->mnemonic);
-  if (mnemonic_len == 0) { FATAL("No mnemonic found"); };
+  if (mnemonic_len == 0) { FFATAL("No mnemonic found"); };
 
   char last = instr->mnemonic[mnemonic_len - 1];
   switch (last) {
@@ -252,14 +251,14 @@ size_t ctx_get_size(const cs_insn *instr, cs_arm64_op *operand) {
 
     if (mnemonic_len < 3) {
 
-      FATAL("VAS Mnemonic too short: %s\n", instr->mnemonic);
+      FFATAL("VAS Mnemonic too short: %s\n", instr->mnemonic);
 
     }
 
     vas_digit = instr->mnemonic[2];
     if (vas_digit < '0' || vas_digit > '9') {
 
-      FATAL("VAS Mnemonic digit out of range: %s\n", instr->mnemonic);
+      FFATAL("VAS Mnemonic digit out of range: %s\n", instr->mnemonic);
 
     }
 
@@ -293,7 +292,7 @@ size_t ctx_get_size(const cs_insn *instr, cs_arm64_op *operand) {
     case ARM64_VAS_16B:
       return 16 * count_byte;
     default:
-      FATAL("Unexpected VAS type: %s %d", instr->mnemonic, operand->vas);
+      FFATAL("Unexpected VAS type: %s %d", instr->mnemonic, operand->vas);
 
   }
 
diff --git a/frida_mode/src/ctx/ctx_x64.c b/frida_mode/src/ctx/ctx_x64.c
index da5cb13a..02423416 100644
--- a/frida_mode/src/ctx/ctx_x64.c
+++ b/frida_mode/src/ctx/ctx_x64.c
@@ -1,8 +1,7 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "ctx.h"
+#include "util.h"
 
 #if defined(__x86_64__)
 
@@ -121,7 +120,7 @@ gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
     X86_REG_64(X86_REG_RIP, ctx->rip)
 
     default:
-      FATAL("Failed to read register: %d", reg);
+      FFATAL("Failed to read register: %d", reg);
       return 0;
 
   }
diff --git a/frida_mode/src/ctx/ctx_x86.c b/frida_mode/src/ctx/ctx_x86.c
index 1a587702..438e1fde 100644
--- a/frida_mode/src/ctx/ctx_x86.c
+++ b/frida_mode/src/ctx/ctx_x86.c
@@ -1,8 +1,7 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "ctx.h"
+#include "util.h"
 
 #if defined(__i386__)
 
@@ -72,7 +71,7 @@ gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
     X86_REG_32(X86_REG_EIP, ctx->eip)
 
     default:
-      FATAL("Failed to read register: %d", reg);
+      FFATAL("Failed to read register: %d", reg);
       return 0;
 
   }
diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c
index 186ddd3a..de645fdb 100644
--- a/frida_mode/src/entry.c
+++ b/frida_mode/src/entry.c
@@ -1,8 +1,10 @@
 #include <dlfcn.h>
 
-#include "frida-gumjs.h"
+#if defined(__linux__) && !defined(__ANDROID__)
+  #include <sys/prctl.h>
+#endif
 
-#include "debug.h"
+#include "frida-gumjs.h"
 
 #include "entry.h"
 #include "instrument.h"
@@ -16,33 +18,61 @@
 extern void __afl_manual_init();
 
 guint64  entry_point = 0;
+gboolean traceable = FALSE;
 gboolean entry_compiled = FALSE;
 gboolean entry_run = FALSE;
 
 static void entry_launch(void) {
 
-  OKF("Entry point reached");
+  FOKF("Entry point reached");
   __afl_manual_init();
 
   /* Child here */
   entry_run = TRUE;
+  entry_on_fork();
   instrument_on_fork();
   seccomp_on_fork();
   stats_on_fork();
 
 }
 
+#if defined(__linux__) && defined(PR_SET_PTRACER) && !defined(__ANDROID__)
+void entry_on_fork(void) {
+
+  if (traceable) {
+
+    if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) < 0) {
+
+      FFATAL("Failed to PR_SET_PTRACER");
+
+    }
+
+  }
+
+}
+
+#else
+void entry_on_fork(void) {
+
+  if (traceable) { FWARNF("AFL_FRIDA_TRACEABLE unsupported"); }
+
+}
+
+#endif
+
 void entry_config(void) {
 
-  entry_point = util_read_address("AFL_ENTRYPOINT");
+  entry_point = util_read_address("AFL_ENTRYPOINT", 0);
+  if (getenv("AFL_FRIDA_TRACEABLE") != NULL) { traceable = TRUE; }
 
 }
 
 void entry_init(void) {
 
-  OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_point);
+  FOKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_point);
+  FOKF("dumpable: [%c]", traceable ? 'X' : ' ');
 
-  if (dlopen(NULL, RTLD_NOW) == NULL) { FATAL("Failed to dlopen: %d", errno); }
+  if (dlopen(NULL, RTLD_NOW) == NULL) { FFATAL("Failed to dlopen: %d", errno); }
 
 }
 
@@ -64,7 +94,7 @@ static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
 void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
 
   UNUSED_PARAMETER(output);
-  OKF("AFL_ENTRYPOINT reached");
+  FOKF("AFL_ENTRYPOINT reached");
 
   if (persistent_start == 0) {
 
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index fd0982f8..8ee21f5b 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -6,7 +6,6 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 #include "hash.h"
 
 #include "asan.h"
@@ -69,7 +68,8 @@ guint64 instrument_get_offset_hash(GumAddress current_rip) {
 
   guint64 area_offset = hash64((unsigned char *)&current_rip,
                                sizeof(GumAddress), instrument_hash_seed);
-  return area_offset &= MAP_SIZE - 1;
+  gsize   map_size_pow2 = util_log2(__afl_map_size);
+  return area_offset &= ((1 << map_size_pow2) - 1);
 
 }
 
@@ -135,8 +135,8 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
   previous_rip = current_rip;
   previous_end = current_end;
 
-  instrument_previous_pc = ((current_pc & (MAP_SIZE - 1) >> 1)) |
-                           ((current_pc & 0x1) << (MAP_SIZE_POW2 - 1));
+  gsize map_size_pow2 = util_log2(__afl_map_size);
+  instrument_previous_pc = util_rotate(current_pc, 1, map_size_pow2);
 
 }
 
@@ -193,7 +193,20 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
       instrument_debug_start(instr->address, output);
       instrument_coverage_start(instr->address);
 
+#if defined(__arm__)
+      if (output->encoding == GUM_INSTRUCTION_SPECIAL) {
+
+        prefetch_write(GSIZE_TO_POINTER(instr->address + 1));
+
+      } else {
+
+        prefetch_write(GSIZE_TO_POINTER(instr->address));
+
+      }
+
+#else
       prefetch_write(GSIZE_TO_POINTER(instr->address));
+#endif
 
       if (likely(!excluded)) {
 
@@ -213,7 +226,7 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
 
     }
 
-    instrument_debug_instruction(instr->address, instr->size);
+    instrument_debug_instruction(instr->address, instr->size, output);
 
     if (likely(!excluded)) {
 
@@ -246,7 +259,7 @@ void instrument_config(void) {
   instrument_tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
   instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL);
   instrument_use_fixed_seed = (getenv("AFL_FRIDA_INST_SEED") != NULL);
-  instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED");
+  instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED", 0);
   instrument_coverage_unstable_filename =
       (getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE"));
 
@@ -261,14 +274,14 @@ void instrument_init(void) {
 
   if (!instrument_is_coverage_optimize_supported()) instrument_optimize = false;
 
-  OKF("Instrumentation - optimize [%c]", instrument_optimize ? 'X' : ' ');
-  OKF("Instrumentation - tracing [%c]", instrument_tracing ? 'X' : ' ');
-  OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' ');
-  OKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]",
-      instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed);
-  OKF("Instrumentation - unstable coverage [%c] [%s]",
-      instrument_coverage_unstable_filename == NULL ? ' ' : 'X',
-      instrument_coverage_unstable_filename);
+  FOKF("Instrumentation - optimize [%c]", instrument_optimize ? 'X' : ' ');
+  FOKF("Instrumentation - tracing [%c]", instrument_tracing ? 'X' : ' ');
+  FOKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' ');
+  FOKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]",
+       instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed);
+  FOKF("Instrumentation - unstable coverage [%c] [%s]",
+       instrument_coverage_unstable_filename == NULL ? ' ' : 'X',
+       instrument_coverage_unstable_filename);
 
   if (instrument_tracing && instrument_optimize) {
 
@@ -304,7 +317,8 @@ void instrument_init(void) {
 
   if (instrument_unique) {
 
-    int shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
+    int shm_id =
+        shmget(IPC_PRIVATE, __afl_map_size, IPC_CREAT | IPC_EXCL | 0600);
     if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
 
     edges_notified = shmat(shm_id, NULL, 0);
@@ -321,7 +335,7 @@ void instrument_init(void) {
     }
 
     /* Clear it, not sure it's necessary, just seems like good practice */
-    memset(edges_notified, '\0', MAP_SIZE);
+    memset(edges_notified, '\0', __afl_map_size);
 
   }
 
@@ -341,15 +355,22 @@ void instrument_init(void) {
      * parallel fuzzing. The seed itself, doesn't have to be random, it
      * just needs to be different for each instance.
      */
-    instrument_hash_seed = g_get_monotonic_time() ^
-                           (((guint64)getpid()) << 32) ^ syscall(SYS_gettid);
+    guint64 tid;
+#if defined(__APPLE__)
+    pthread_threadid_np(NULL, &tid);
+#else
+    tid = syscall(SYS_gettid);
+#endif
+    instrument_hash_seed =
+        g_get_monotonic_time() ^ (((guint64)getpid()) << 32) ^ tid;
 
   }
 
-  OKF("Instrumentation - seed [0x%016" G_GINT64_MODIFIER "x]",
-      instrument_hash_seed);
+  FOKF("Instrumentation - seed [0x%016" G_GINT64_MODIFIER "x]",
+       instrument_hash_seed);
   instrument_hash_zero = instrument_get_offset_hash(0);
 
+  instrument_coverage_optimize_init();
   instrument_debug_init();
   instrument_coverage_init();
   asan_init();
diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c
index 0e15940a..16e8eaab 100644
--- a/frida_mode/src/instrument/instrument_arm32.c
+++ b/frida_mode/src/instrument/instrument_arm32.c
@@ -1,7 +1,5 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "instrument.h"
 #include "util.h"
 
@@ -18,13 +16,27 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
   UNUSED_PARAMETER(instr);
   UNUSED_PARAMETER(output);
-  FATAL("Optimized coverage not supported on this architecture");
+  FFATAL("Optimized coverage not supported on this architecture");
+
+}
+
+void instrument_coverage_optimize_init(void) {
+
+  FWARNF("Optimized coverage not supported on this architecture");
 
 }
 
 void instrument_flush(GumStalkerOutput *output) {
 
-  gum_arm_writer_flush(output->writer.arm);
+  if (output->encoding == GUM_INSTRUCTION_SPECIAL) {
+
+    gum_thumb_writer_flush(output->writer.thumb);
+
+  } else {
+
+    gum_arm_writer_flush(output->writer.arm);
+
+  }
 
 }
 
diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c
index cf37e048..0f635458 100644
--- a/frida_mode/src/instrument/instrument_arm64.c
+++ b/frida_mode/src/instrument/instrument_arm64.c
@@ -1,7 +1,6 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 
 #include "instrument.h"
 
@@ -95,6 +94,10 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
 }
 
+void instrument_coverage_optimize_init(void) {
+
+}
+
 void instrument_flush(GumStalkerOutput *output) {
 
   gum_arm64_writer_flush(output->writer.arm64);
diff --git a/frida_mode/src/instrument/instrument_coverage.c b/frida_mode/src/instrument/instrument_coverage.c
index 46c816bc..c1984eb2 100644
--- a/frida_mode/src/instrument/instrument_coverage.c
+++ b/frida_mode/src/instrument/instrument_coverage.c
@@ -5,8 +5,6 @@
 
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "instrument.h"
 #include "util.h"
 
@@ -239,7 +237,7 @@ static void instrument_coverage_mark(void *key, void *value, void *user_data) {
 
 }
 
-static void coverage_write(void *data, size_t size) {
+static void coverage_write(int fd, void *data, size_t size) {
 
   ssize_t written;
   size_t  remain = size;
@@ -247,11 +245,11 @@ static void coverage_write(void *data, size_t size) {
   for (char *cursor = (char *)data; remain > 0;
        remain -= written, cursor += written) {
 
-    written = write(normal_coverage_fd, cursor, remain);
+    written = write(fd, cursor, remain);
 
     if (written < 0) {
 
-      FATAL("Coverage - Failed to write: %s (%d)\n", (char *)data, errno);
+      FFATAL("Coverage - Failed to write: %s (%d)\n", (char *)data, errno);
 
     }
 
@@ -259,7 +257,7 @@ static void coverage_write(void *data, size_t size) {
 
 }
 
-static void coverage_format(char *format, ...) {
+static void coverage_format(int fd, char *format, ...) {
 
   va_list ap;
   char    buffer[4096] = {0};
@@ -274,11 +272,11 @@ static void coverage_format(char *format, ...) {
 
   len = strnlen(buffer, sizeof(buffer));
 
-  coverage_write(buffer, len);
+  coverage_write(fd, buffer, len);
 
 }
 
-static void coverage_write_modules(GArray *coverage_modules) {
+static void coverage_write_modules(int fd, GArray *coverage_modules) {
 
   guint emitted = 0;
   for (guint i = 0; i < coverage_modules->len; i++) {
@@ -287,16 +285,16 @@ static void coverage_write_modules(GArray *coverage_modules) {
         &g_array_index(coverage_modules, coverage_range_t, i);
     if (module->count == 0) continue;
 
-    coverage_format("%3u, ", emitted);
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", module->base_address);
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", module->limit);
+    coverage_format(fd, "%3u, ", emitted);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->base_address);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->limit);
     /* entry */
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", 0);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0);
     /* checksum */
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", 0);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0);
     /* timestamp */
-    coverage_format("%08" G_GINT32_MODIFIER "X, ", 0);
-    coverage_format("%s\n", module->path);
+    coverage_format(fd, "%08" G_GINT32_MODIFIER "X, ", 0);
+    coverage_format(fd, "%s\n", module->path);
     emitted++;
 
   }
@@ -306,7 +304,7 @@ static void coverage_write_modules(GArray *coverage_modules) {
 static void coverage_write_events(void *key, void *value, void *user_data) {
 
   UNUSED_PARAMETER(key);
-  UNUSED_PARAMETER(user_data);
+  int                     fd = *((int *)user_data);
   normal_coverage_data_t *val = (normal_coverage_data_t *)value;
 
   if (val->module == NULL) { return; }
@@ -319,20 +317,20 @@ static void coverage_write_events(void *key, void *value, void *user_data) {
 
   };
 
-  coverage_write(&evt, sizeof(coverage_event_t));
+  coverage_write(fd, &evt, sizeof(coverage_event_t));
 
 }
 
-static void coverage_write_header(guint coverage_marked_modules) {
+static void coverage_write_header(int fd, guint coverage_marked_modules) {
 
   char version[] = "DRCOV VERSION: 2\n";
   char flavour[] = "DRCOV FLAVOR: frida\n";
   char columns[] = "Columns: id, base, end, entry, checksum, timestamp, path\n";
-  coverage_write(version, sizeof(version) - 1);
-  coverage_write(flavour, sizeof(flavour) - 1);
-  coverage_format("Module Table: version 2, count %u\n",
+  coverage_write(fd, version, sizeof(version) - 1);
+  coverage_write(fd, flavour, sizeof(flavour) - 1);
+  coverage_format(fd, "Module Table: version 2, count %u\n",
                   coverage_marked_modules);
-  coverage_write(columns, sizeof(columns) - 1);
+  coverage_write(fd, columns, sizeof(columns) - 1);
 
 }
 
@@ -371,7 +369,7 @@ static void instrument_coverage_normal_run() {
 
   if (close(normal_coverage_pipes[STDOUT_FILENO]) != 0) {
 
-    FATAL("Failed to close parent read pipe");
+    FFATAL("Failed to close parent read pipe");
 
   }
 
@@ -379,7 +377,7 @@ static void instrument_coverage_normal_run() {
       g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
   if (coverage_hash == NULL) {
 
-    FATAL("Failed to g_hash_table_new, errno: %d", errno);
+    FFATAL("Failed to g_hash_table_new, errno: %d", errno);
 
   }
 
@@ -396,7 +394,7 @@ static void instrument_coverage_normal_run() {
 
   }
 
-  if (bytes != 0) { FATAL("Coverage data truncated"); }
+  if (bytes != 0) { FFATAL("Coverage data truncated"); }
 
   instrument_coverage_print("Coverage - Preparing\n");
 
@@ -414,10 +412,11 @@ static void instrument_coverage_normal_run() {
   instrument_coverage_print("Coverage - Marked Modules: %u\n",
                             coverage_marked_modules);
 
-  coverage_write_header(coverage_marked_modules);
-  coverage_write_modules(coverage_modules);
-  coverage_format("BB Table: %u bbs\n", ctx.count);
-  g_hash_table_foreach(coverage_hash, coverage_write_events, NULL);
+  coverage_write_header(normal_coverage_fd, coverage_marked_modules);
+  coverage_write_modules(normal_coverage_fd, coverage_modules);
+  coverage_format(normal_coverage_fd, "BB Table: %u bbs\n", ctx.count);
+  g_hash_table_foreach(coverage_hash, coverage_write_events,
+                       &normal_coverage_fd);
 
   g_hash_table_unref(coverage_hash);
 
@@ -435,7 +434,7 @@ static GArray *instrument_coverage_unstable_read_unstable_ids(void) {
   if (!g_file_get_contents(unstable_coverage_fuzzer_stats, &contents, &length,
                            NULL)) {
 
-    FATAL("Failed to read fuzzer_stats");
+    FFATAL("Failed to read fuzzer_stats");
 
   }
 
@@ -526,7 +525,7 @@ static GHashTable *instrument_collect_unstable_blocks(
     GHashTable *child =
         (GHashTable *)g_hash_table_lookup(unstable_coverage_hash, *id);
 
-    if (child == NULL) { FATAL("Failed to find edge ID"); }
+    if (child == NULL) { FFATAL("Failed to find edge ID"); }
 
     GHashTableIter iter = {0};
     gpointer       value;
@@ -565,7 +564,7 @@ static void instrument_coverage_unstable_run(void) {
 
   if (close(unstable_coverage_pipes[STDOUT_FILENO]) != 0) {
 
-    FATAL("Failed to close parent read pipe");
+    FFATAL("Failed to close parent read pipe");
 
   }
 
@@ -573,7 +572,7 @@ static void instrument_coverage_unstable_run(void) {
       g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_hash_table_unref);
   if (unstable_coverage_hash == NULL) {
 
-    FATAL("Failed to g_hash_table_new, errno: %d", errno);
+    FFATAL("Failed to g_hash_table_new, errno: %d", errno);
 
   }
 
@@ -599,7 +598,7 @@ static void instrument_coverage_unstable_run(void) {
       if (!g_hash_table_insert(unstable_coverage_hash,
                                GSIZE_TO_POINTER(value->edge), hash_value)) {
 
-        FATAL("Entry already in hashtable");
+        FFATAL("Entry already in hashtable");
 
       }
 
@@ -613,7 +612,7 @@ static void instrument_coverage_unstable_run(void) {
 
   }
 
-  if (bytes != 0) { FATAL("Unstable coverage data truncated"); }
+  if (bytes != 0) { FFATAL("Unstable coverage data truncated"); }
 
   instrument_coverage_print("Coverage - Preparing\n");
 
@@ -638,10 +637,11 @@ static void instrument_coverage_unstable_run(void) {
   instrument_coverage_print("Coverage - Marked Modules: %u\n",
                             coverage_marked_modules);
 
-  coverage_write_header(coverage_marked_modules);
-  coverage_write_modules(coverage_modules);
-  coverage_format("BB Table: %u bbs\n", ctx.count);
-  g_hash_table_foreach(unstable_blocks, coverage_write_events, NULL);
+  coverage_write_header(unstable_coverage_fd, coverage_marked_modules);
+  coverage_write_modules(unstable_coverage_fd, coverage_modules);
+  coverage_format(unstable_coverage_fd, "BB Table: %u bbs\n", ctx.count);
+  g_hash_table_foreach(unstable_blocks, coverage_write_events,
+                       &unstable_coverage_fd);
 
   g_hash_table_unref(unstable_blocks);
   g_array_free(unstable_edge_ids, TRUE);
@@ -659,33 +659,33 @@ void instrument_coverage_config(void) {
 
 void instrument_coverage_normal_init(void) {
 
-  OKF("Coverage - enabled [%c]",
-      instrument_coverage_filename == NULL ? ' ' : 'X');
+  FOKF("Coverage - enabled [%c]",
+       instrument_coverage_filename == NULL ? ' ' : 'X');
 
   if (instrument_coverage_filename == NULL) { return; }
 
-  OKF("Coverage - file [%s]", instrument_coverage_filename);
+  FOKF("Coverage - file [%s]", instrument_coverage_filename);
 
   char *path = g_canonicalize_filename(instrument_coverage_filename,
                                        g_get_current_dir());
 
-  OKF("Coverage - path [%s]", path);
+  FOKF("Coverage - path [%s]", path);
 
   normal_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
                             S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 
   if (normal_coverage_fd < 0) {
 
-    FATAL("Failed to open coverage file '%s'", path);
+    FFATAL("Failed to open coverage file '%s'", path);
 
   }
 
   g_free(path);
 
-  if (pipe(normal_coverage_pipes) != 0) { FATAL("Failed to create pipes"); }
+  if (pipe(normal_coverage_pipes) != 0) { FFATAL("Failed to create pipes"); }
 
   pid_t pid = fork();
-  if (pid == -1) { FATAL("Failed to start coverage process"); }
+  if (pid == -1) { FFATAL("Failed to start coverage process"); }
 
   if (pid == 0) {
 
@@ -697,13 +697,13 @@ void instrument_coverage_normal_init(void) {
 
   if (close(normal_coverage_fd) < 0) {
 
-    FATAL("Failed to close coverage output file");
+    FFATAL("Failed to close coverage output file");
 
   }
 
   if (close(normal_coverage_pipes[STDIN_FILENO]) != 0) {
 
-    FATAL("Failed to close parent read pipe");
+    FFATAL("Failed to close parent read pipe");
 
   }
 
@@ -711,15 +711,14 @@ void instrument_coverage_normal_init(void) {
 
 void instrument_coverage_unstable_find_output(void) {
 
-  pid_t  parent = getpid();
   gchar *fds_name = g_strdup_printf("/proc/%d/fd/", getppid());
 
   gchar *root = g_file_read_link("/proc/self/root", NULL);
-  if (root == NULL) { FATAL("Failed to read link"); }
+  if (root == NULL) { FFATAL("Failed to read link"); }
 
   GDir *dir = g_dir_open(fds_name, 0, NULL);
 
-  OKF("Coverage Unstable - fds: %s", fds_name);
+  FOKF("Coverage Unstable - fds: %s", fds_name);
 
   for (const gchar *filename = g_dir_read_name(dir); filename != NULL;
        filename = g_dir_read_name(dir)) {
@@ -727,7 +726,7 @@ void instrument_coverage_unstable_find_output(void) {
     gchar *fullname = g_build_path("/", fds_name, filename, NULL);
 
     gchar *link = g_file_read_link(fullname, NULL);
-    if (link == NULL) { FATAL("Failed to read link: %s", fullname); }
+    if (link == NULL) { FFATAL("Failed to read link: %s", fullname); }
 
     gchar *basename = g_path_get_basename(link);
     if (g_strcmp0(basename, "default") != 0) {
@@ -779,11 +778,11 @@ void instrument_coverage_unstable_find_output(void) {
 
   if (unstable_coverage_fuzzer_stats == NULL) {
 
-    FATAL("Failed to find fuzzer stats");
+    FFATAL("Failed to find fuzzer stats");
 
   }
 
-  OKF("Fuzzer stats: %s", unstable_coverage_fuzzer_stats);
+  FOKF("Fuzzer stats: %s", unstable_coverage_fuzzer_stats);
 
 }
 
@@ -794,14 +793,14 @@ void instrument_coverage_unstable_init(void) {
   char *path = g_canonicalize_filename(instrument_coverage_unstable_filename,
                                        g_get_current_dir());
 
-  OKF("Coverage - unstable path [%s]", instrument_coverage_unstable_filename);
+  FOKF("Coverage - unstable path [%s]", instrument_coverage_unstable_filename);
 
   unstable_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
                               S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 
   if (unstable_coverage_fd < 0) {
 
-    FATAL("Failed to open unstable coverage file '%s'", path);
+    FFATAL("Failed to open unstable coverage file '%s'", path);
 
   }
 
@@ -811,12 +810,12 @@ void instrument_coverage_unstable_init(void) {
 
   if (pipe(unstable_coverage_pipes) != 0) {
 
-    FATAL("Failed to create unstable pipes");
+    FFATAL("Failed to create unstable pipes");
 
   }
 
   pid_t pid = fork();
-  if (pid == -1) { FATAL("Failed to start coverage process"); }
+  if (pid == -1) { FFATAL("Failed to start coverage process"); }
 
   if (pid == 0) {
 
@@ -828,13 +827,13 @@ void instrument_coverage_unstable_init(void) {
 
   if (close(unstable_coverage_fd) < 0) {
 
-    FATAL("Failed to close unstable coverage output file");
+    FFATAL("Failed to close unstable coverage output file");
 
   }
 
   if (close(unstable_coverage_pipes[STDIN_FILENO]) != 0) {
 
-    FATAL("Failed to close parent read pipe");
+    FFATAL("Failed to close parent read pipe");
 
   }
 
@@ -866,7 +865,7 @@ void instrument_coverage_end(uint64_t address) {
   if (write(normal_coverage_pipes[STDOUT_FILENO], &data,
             sizeof(normal_coverage_data_t)) != sizeof(normal_coverage_data_t)) {
 
-    FATAL("Coverage I/O error");
+    FFATAL("Coverage I/O error");
 
   }
 
@@ -889,7 +888,7 @@ void instrument_coverage_unstable(guint64 edge, guint64 previous_rip,
             sizeof(unstable_coverage_data_t)) !=
       sizeof(unstable_coverage_data_t)) {
 
-    FATAL("Unstable coverage I/O error");
+    FFATAL("Unstable coverage I/O error");
 
   }
 
diff --git a/frida_mode/src/instrument/instrument_debug.c b/frida_mode/src/instrument/instrument_debug.c
index b8cca634..9c95857f 100644
--- a/frida_mode/src/instrument/instrument_debug.c
+++ b/frida_mode/src/instrument/instrument_debug.c
@@ -5,8 +5,6 @@
 
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "instrument.h"
 #include "util.h"
 
@@ -34,18 +32,27 @@ static void instrument_debug(char *format, ...) {
 
 }
 
-static void instrument_disasm(guint8 *start, guint8 *end) {
+static void instrument_disasm(guint8 *start, guint8 *end,
+                              GumStalkerOutput *output) {
 
   csh      capstone;
   cs_err   err;
+  cs_mode  mode;
   uint16_t size;
   cs_insn *insn;
   size_t   count = 0;
   size_t   i;
   uint16_t len;
 
+  mode = GUM_DEFAULT_CS_MODE | GUM_DEFAULT_CS_ENDIAN;
+
+#if defined(__arm__)
+  if (output->encoding == GUM_INSTRUCTION_SPECIAL) { mode |= CS_MODE_THUMB; }
+#endif
+
   err = cs_open(GUM_DEFAULT_CS_ARCH,
-                GUM_DEFAULT_CS_MODE | GUM_DEFAULT_CS_ENDIAN, &capstone);
+                CS_MODE_THUMB | GUM_DEFAULT_CS_MODE | GUM_DEFAULT_CS_ENDIAN,
+                &capstone);
   g_assert(err == CS_ERR_OK);
 
   size = GPOINTER_TO_SIZE(end) - GPOINTER_TO_SIZE(start);
@@ -89,24 +96,24 @@ void instrument_debug_config(void) {
 
 void instrument_debug_init(void) {
 
-  OKF("Instrumentation debugging - enabled [%c]",
-      instrument_debug_filename == NULL ? ' ' : 'X');
+  FOKF("Instrumentation debugging - enabled [%c]",
+       instrument_debug_filename == NULL ? ' ' : 'X');
 
   if (instrument_debug_filename == NULL) { return; }
 
-  OKF("Instrumentation debugging - file [%s]", instrument_debug_filename);
+  FOKF("Instrumentation debugging - file [%s]", instrument_debug_filename);
 
   if (instrument_debug_filename == NULL) { return; }
 
   char *path =
       g_canonicalize_filename(instrument_debug_filename, g_get_current_dir());
 
-  OKF("Instrumentation debugging - path [%s]", path);
+  FOKF("Instrumentation debugging - path [%s]", path);
 
   debugging_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
                       S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 
-  if (debugging_fd < 0) { FATAL("Failed to open stats file '%s'", path); }
+  if (debugging_fd < 0) { FFATAL("Failed to open stats file '%s'", path); }
 
   g_free(path);
 
@@ -123,11 +130,12 @@ void instrument_debug_start(uint64_t address, GumStalkerOutput *output) {
 
 }
 
-void instrument_debug_instruction(uint64_t address, uint16_t size) {
+void instrument_debug_instruction(uint64_t address, uint16_t size,
+                                  GumStalkerOutput *output) {
 
   if (likely(debugging_fd < 0)) { return; }
   uint8_t *start = (uint8_t *)GSIZE_TO_POINTER(address);
-  instrument_disasm(start, start + size);
+  instrument_disasm(start, start + size, output);
 
 }
 
@@ -138,7 +146,7 @@ void instrument_debug_end(GumStalkerOutput *output) {
 
   instrument_debug("\nGenerated block %p-%p\n", instrument_gen_start,
                    instrument_gen_end);
-  instrument_disasm(instrument_gen_start, instrument_gen_end);
+  instrument_disasm(instrument_gen_start, instrument_gen_end, output);
 
 }
 
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index fec8afbb..41162f2a 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -1,85 +1,343 @@
+#include <fcntl.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+
+#if defined(__linux__)
+  #if !defined(__ANDROID__)
+    #include <sys/prctl.h>
+    #include <sys/syscall.h>
+  #else
+    #include <linux/ashmem.h>
+  #endif
+#endif
+
 #include "frida-gumjs.h"
 
 #include "config.h"
 
 #include "instrument.h"
+#include "ranges.h"
+#include "stalker.h"
+#include "util.h"
 
 #if defined(__x86_64__)
 
-static GumAddress current_log_impl = GUM_ADDRESS(0);
+  #ifndef MAP_FIXED_NOREPLACE
+    #ifdef MAP_EXCL
+      #define MAP_FIXED_NOREPLACE MAP_EXCL | MAP_FIXED
+    #else
+      #define MAP_FIXED_NOREPLACE MAP_FIXED
+    #endif
+  #endif
 
-static const guint8 afl_log_code[] = {
+static GHashTable *coverage_blocks = NULL;
 
-    0x9c,                                                         /* pushfq */
-    0x51,                                                       /* push rcx */
-    0x52,                                                       /* push rdx */
+gboolean instrument_is_coverage_optimize_supported(void) {
 
-    0x48, 0x8b, 0x0d, 0x26,
-    0x00, 0x00, 0x00,                          /* mov rcx, sym.&previous_pc */
-    0x48, 0x8b, 0x11,                               /* mov rdx, qword [rcx] */
-    0x48, 0x31, 0xfa,                                       /* xor rdx, rdi */
+  return true;
 
-    0x48, 0x03, 0x15, 0x11,
-    0x00, 0x00, 0x00,                     /* add rdx, sym._afl_area_ptr_ptr */
+}
 
-    0x80, 0x02, 0x01,                              /* add byte ptr [rdx], 1 */
-    0x80, 0x12, 0x00,                              /* adc byte ptr [rdx], 0 */
-    0x66, 0xd1, 0xcf,                                          /* ror di, 1 */
-    0x48, 0x89, 0x39,                               /* mov qword [rcx], rdi */
+static gboolean instrument_coverage_in_range(gssize offset) {
 
-    0x5a,                                                        /* pop rdx */
-    0x59,                                                        /* pop rcx */
-    0x9d,                                                          /* popfq */
+  return (offset >= G_MININT32 && offset <= G_MAXINT32);
 
-    0xc3,                                                            /* ret */
+}
 
-    0x90
+  #pragma pack(push, 1)
+typedef struct {
 
-    /* Read-only data goes here: */
-    /* uint8_t* __afl_area_ptr */
-    /* uint64_t* &previous_pc */
+  // cur_location = (block_address >> 4) ^ (block_address << 8);
+  // shared_mem[cur_location ^ prev_location]++;
+  // prev_location = cur_location >> 1;
 
-};
+  //  mov    QWORD PTR [rsp-0x80],rax
+  //  lahf
+  //  mov    QWORD PTR [rsp-0x88],rax
+  //  mov    QWORD PTR [rsp-0x90],rbx
+  //  mov    eax,DWORD PTR [rip+0x333d5a]        # 0x7ffff6ff2740
+  //  mov    DWORD PTR [rip+0x333d3c],0x9fbb        # 0x7ffff6ff2740
+  //  xor    eax,0x103f77
+  //  mov    bl,BYTE PTR [rax]
+  //  add    bl,0x1
+  //  adc    bl,0x0
+  //  mov    BYTE PTR [rax],bl
+  //  mov    rbx,QWORD PTR [rsp-0x90]
+  //  mov    rax,QWORD PTR [rsp-0x88]
+  //  sahf
+  //  mov    rax,QWORD PTR [rsp-0x80]
 
-gboolean instrument_is_coverage_optimize_supported(void) {
+  uint8_t mov_rax_rsp_88[8];
+  uint8_t lahf;
+  uint8_t mov_rax_rsp_90[8];
+  uint8_t mov_rbx_rsp_98[8];
 
-  return true;
+  uint8_t mov_eax_prev_loc[6];
+  uint8_t mov_prev_loc_curr_loc_shr1[10];
+
+  uint8_t xor_eax_curr_loc[5];
+
+  uint8_t mov_rbx_ptr_rax[2];
+  uint8_t add_bl_1[3];
+  uint8_t adc_bl_0[3];
+  uint8_t mov_ptr_rax_rbx[2];
+
+  uint8_t mov_rsp_98_rbx[8];
+  uint8_t mov_rsp_90_rax[8];
+  uint8_t sahf;
+  uint8_t mov_rsp_88_rax[8];
+
+} 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_rax_rsp_88 = {0x48, 0x89, 0x84, 0x24, 0x78, 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},
+
+        .xor_eax_curr_loc = {0x35},
+        .mov_rbx_ptr_rax = {0x8a, 0x18},
+        .add_bl_1 = {0x80, 0xc3, 0x01},
+        .adc_bl_0 = {0x80, 0xd3, 0x00},
+        .mov_ptr_rax_rbx = {0x88, 0x18},
+
+        .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_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
 
 }
 
-static guint8 align_pad[] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
+;
 
-static void instrument_coverate_write_function(GumStalkerOutput *output) {
+static gboolean instrument_coverage_find_low(const GumRangeDetails *details,
+                                             gpointer               user_data) {
 
-  guint64       misalign = 0;
-  GumX86Writer *cw = output->writer.x86;
+  static GumAddress last_limit = (64ULL << 10);
+  gpointer *        address = (gpointer *)user_data;
+
+  if ((details->range->base_address - last_limit) > __afl_map_size) {
+
+    *address = GSIZE_TO_POINTER(last_limit);
+    return FALSE;
+
+  }
+
+  if (details->range->base_address > ((2ULL << 30) - __afl_map_size)) {
+
+    return FALSE;
+
+  }
+
+  /*
+   * Align our buffer on a 64k boundary so that the low 16-bits of the address
+   * are zero, then we can just XOR the base address in, when we XOR with the
+   * current block ID.
+   */
+  last_limit = GUM_ALIGN_SIZE(
+      details->range->base_address + details->range->size, (64ULL << 10));
+  return TRUE;
+
+}
+
+static void instrument_coverage_optimize_map_mmap_anon(gpointer address) {
+
+  __afl_area_ptr =
+      mmap(address, __afl_map_size, PROT_READ | PROT_WRITE,
+           MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+  if (__afl_area_ptr != address) {
+
+    FATAL("Failed to map mmap __afl_area_ptr: %d", errno);
+
+  }
+
+}
+
+static void instrument_coverage_optimize_map_mmap(char *   shm_file_path,
+                                                  gpointer address) {
+
+  int shm_fd = -1;
+
+  if (munmap(__afl_area_ptr, __afl_map_size) != 0) {
+
+    FATAL("Failed to unmap previous __afl_area_ptr");
+
+  }
+
+  __afl_area_ptr = NULL;
+
+  #if !defined(__ANDROID__)
+  shm_fd = shm_open(shm_file_path, O_RDWR, DEFAULT_PERMISSION);
+  if (shm_fd == -1) { FATAL("shm_open() failed\n"); }
+  #else
+  shm_fd = open("/dev/ashmem", O_RDWR);
+  if (shm_fd == -1) { FATAL("open() failed\n"); }
+  if (ioctl(shm_fd, ASHMEM_SET_NAME, shm_file_path) == -1) {
+
+    FATAL("ioctl(ASHMEM_SET_NAME) failed");
+
+  }
+
+  if (ioctl(shm_fd, ASHMEM_SET_SIZE, __afl_map_size) == -1) {
+
+    FATAL("ioctl(ASHMEM_SET_SIZE) failed");
+
+  }
+
+  #endif
+
+  __afl_area_ptr = mmap(address, __afl_map_size, PROT_READ | PROT_WRITE,
+                        MAP_FIXED_NOREPLACE | MAP_SHARED, shm_fd, 0);
+  if (__afl_area_ptr != address) {
+
+    FATAL("Failed to map mmap __afl_area_ptr: %d", errno);
+
+  }
+
+  if (close(shm_fd) != 0) { FATAL("Failed to close shm_fd"); }
+
+}
+
+static void instrument_coverage_optimize_map_shm(guint64  shm_env_val,
+                                                 gpointer address) {
+
+  if (shmdt(__afl_area_ptr) != 0) {
+
+    FATAL("Failed to detach previous __afl_area_ptr");
+
+  }
 
-  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)) {
+  __afl_area_ptr = shmat(shm_env_val, address, 0);
+  if (__afl_area_ptr != address) {
 
-    gconstpointer after_log_impl = cw->code + 1;
+    FATAL("Failed to map shm __afl_area_ptr: %d", errno);
 
-    gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
+  }
+
+}
+
+static void instrument_coverage_switch(GumStalkerObserver *self,
+                                       gpointer            start_address,
+                                       const cs_insn *     from_insn,
+                                       gpointer *          target) {
+
+  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;
+
+  }
+
+  *target = (guint8 *)*target + sizeof(afl_log_code);
+
+}
+
+void instrument_coverage_optimize_init(void) {
+
+  gpointer low_address = NULL;
+
+  gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, instrument_coverage_find_low,
+                               &low_address);
+
+  FOKF("Low address: %p", low_address);
+
+  if (low_address == 0 ||
+      GPOINTER_TO_SIZE(low_address) > ((2UL << 20) - __afl_map_size)) {
+
+    FATAL("Invalid low_address: %p", low_address);
+
+  }
+
+  ranges_print_debug_maps();
+
+  char *shm_env = getenv(SHM_ENV_VAR);
+  FOKF("SHM_ENV_VAR: %s", shm_env);
+
+  if (shm_env == NULL) {
+
+    FWARNF("SHM_ENV_VAR not set, using anonymous map for debugging purposes");
 
-    misalign = (cw->pc & 0x7);
-    if (misalign != 0) {
+    instrument_coverage_optimize_map_mmap_anon(low_address);
 
-      gum_x86_writer_put_bytes(cw, align_pad, 8 - misalign);
+  } else {
+
+    guint64 shm_env_val = g_ascii_strtoull(shm_env, NULL, 10);
+
+    if (shm_env_val == 0) {
+
+      instrument_coverage_optimize_map_mmap(shm_env, low_address);
+
+    } else {
+
+      instrument_coverage_optimize_map_shm(shm_env_val, low_address);
 
     }
 
-    current_log_impl = cw->pc;
-    gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
+  }
+
+  FOKF("__afl_area_ptr: %p", __afl_area_ptr);
+  FOKF("instrument_previous_pc: %p", &instrument_previous_pc);
+
+}
 
-    uint64_t *afl_prev_loc_ptr = &instrument_previous_pc;
-    gum_x86_writer_put_bytes(cw, (const guint8 *)&__afl_area_ptr,
-                             sizeof(__afl_area_ptr));
-    gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
-                             sizeof(afl_prev_loc_ptr));
+static void instrument_coverage_suppress_init(void) {
 
-    gum_x86_writer_put_label(cw, after_log_impl);
+  static gboolean initialized = false;
+  if (initialized) { return; }
+  initialized = true;
+
+  GumStalkerObserver *         observer = stalker_get_observer();
+  GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer);
+  iface->switch_callback = instrument_coverage_switch;
+
+  coverage_blocks = g_hash_table_new(g_direct_hash, g_direct_equal);
+  if (coverage_blocks == NULL) {
+
+    FATAL("Failed to g_hash_table_new, errno: %d", errno);
 
   }
 
@@ -88,18 +346,73 @@ 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);
-
-  gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
-                                        -GUM_RED_ZONE_SIZE);
-  gum_x86_writer_put_push_reg(cw, GUM_REG_RDI);
-  gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDI, area_offset);
-  gum_x86_writer_put_call_address(cw, current_log_impl);
-  gum_x86_writer_put_pop_reg(cw, GUM_REG_RDI);
-  gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
-                                        GUM_RED_ZONE_SIZE);
+  gsize   map_size_pow2;
+  gsize   area_offset_ror;
+  GumAddress code_addr = 0;
+
+  instrument_coverage_suppress_init();
+
+  // gum_x86_writer_put_breakpoint(cw);
+  code_addr = cw->pc;
+  if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
+
+    FATAL("Failed - g_hash_table_add");
+
+  }
+
+  code.code = template;
+
+  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 =
+      GPOINTER_TO_SIZE(&instrument_previous_pc) -
+      (code_addr + offsetof(afl_log_code, code.mov_prev_loc_curr_loc_shr1) +
+       sizeof(code.code.mov_prev_loc_curr_loc_shr1));
+  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);
+  if (!instrument_coverage_in_range(prev_loc_value)) {
+
+    FATAL("Patch out of range (current_pc_value1): 0x%016lX", prev_loc_value);
+
+  }
+
+  *((gint *)&code.bytes[prev_loc_value_offset]) = (gint)prev_loc_value;
+
+  gssize prev_loc_value2 =
+      GPOINTER_TO_SIZE(&instrument_previous_pc) -
+      (code_addr + offsetof(afl_log_code, code.mov_eax_prev_loc) +
+       sizeof(code.code.mov_eax_prev_loc));
+  gssize prev_loc_value_offset2 =
+      offsetof(afl_log_code, code.mov_eax_prev_loc) +
+      sizeof(code.code.mov_eax_prev_loc) - sizeof(gint);
+  if (!instrument_coverage_in_range(prev_loc_value)) {
+
+    FATAL("Patch out of range (current_pc_value1): 0x%016lX", prev_loc_value2);
+
+  }
+
+  *((gint *)&code.bytes[prev_loc_value_offset2]) = (gint)prev_loc_value2;
+
+  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)(GPOINTER_TO_SIZE(__afl_area_ptr) | area_offset);
+
+  gum_x86_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
 
 }
 
diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c
index 7bf48f96..ad837e2d 100644
--- a/frida_mode/src/instrument/instrument_x86.c
+++ b/frida_mode/src/instrument/instrument_x86.c
@@ -1,69 +1,144 @@
 #include "frida-gumjs.h"
 
-#include "debug.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;
+
+  #pragma pack(push, 1)
+typedef struct {
+
+  // cur_location = (block_address >> 4) ^ (block_address << 8);
+  // shared_mem[cur_location ^ prev_location]++;
+  // prev_location = cur_location >> 1;
+
+  uint8_t mov_eax_esp_4[4];
+  uint8_t lahf;
+  uint8_t mov_eax_esp_8[4];
+  uint8_t mov_ebx_esp_c[4];
+
+  uint8_t mov_eax_prev_loc[5];
+  uint8_t mov_prev_loc_curr_loc_shr1[10];
+
+  uint8_t xor_eax_curr_loc[5];
+  uint8_t add_eax_area_ptr[5];
+
+  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];
 
-static void instrument_coverage_function(GumX86Writer *cw) {
+  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_pushfx(cw);
-  gum_x86_writer_put_push_reg(cw, GUM_REG_ECX);
-  gum_x86_writer_put_push_reg(cw, GUM_REG_EDX);
+} afl_log_code_asm_t;
 
-  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);
+  #pragma pack(pop)
 
-  gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EDX, GUM_ADDRESS(__afl_area_ptr));
+typedef union {
 
-  /* 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));
+  afl_log_code_asm_t code;
+  uint8_t            bytes[0];
 
-  /* 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));
+} afl_log_code;
 
-  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);
+static const afl_log_code_asm_t template =
+    {
 
-  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);
+        .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;
+
+  }
+
+  *target = (guint8 *)*target + sizeof(afl_log_code);
+
+}
 
-  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)) {
+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);
 
   }
 
@@ -72,14 +147,65 @@ 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_bytes(cw, code.bytes, sizeof(afl_log_code));
+
+}
 
-  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);
+void instrument_coverage_optimize_init(void) {
 
 }
 
diff --git a/frida_mode/src/intercept.c b/frida_mode/src/intercept.c
index ed8d27bd..26d20c50 100644
--- a/frida_mode/src/intercept.c
+++ b/frida_mode/src/intercept.c
@@ -1,8 +1,7 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "intercept.h"
+#include "util.h"
 
 void intercept_hook(void *address, gpointer replacement, gpointer user_data) {
 
@@ -10,7 +9,7 @@ void intercept_hook(void *address, gpointer replacement, gpointer user_data) {
   gum_interceptor_begin_transaction(interceptor);
   GumReplaceReturn ret =
       gum_interceptor_replace(interceptor, address, replacement, user_data);
-  if (ret != GUM_REPLACE_OK) { FATAL("gum_interceptor_attach: %d", ret); }
+  if (ret != GUM_REPLACE_OK) { FFATAL("gum_interceptor_attach: %d", ret); }
   gum_interceptor_end_transaction(interceptor);
 
 }
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
index 40bb4a16..215fbdaf 100644
--- a/frida_mode/src/js/api.js
+++ b/frida_mode/src/js/api.js
@@ -63,6 +63,12 @@ class Afl {
         Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
     }
     /**
+     * See `AFL_FRIDA_INST_NO_BACKPATCH`.
+     */
+    static setBackpatchDisable() {
+        Afl.jsApiSetBackpatchDisable();
+    }
+    /**
      * See `AFL_FRIDA_DEBUG_MAPS`.
      */
     static setDebugMaps() {
@@ -145,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.
@@ -199,6 +212,12 @@ class Afl {
         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.
@@ -243,6 +262,12 @@ class Afl {
         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);
@@ -261,6 +286,7 @@ Afl.jsApiAddIncludeRange = Afl.jsApiGetFunction("js_api_add_include_range", "voi
 Afl.jsApiAflSharedMemFuzzing = Afl.jsApiGetSymbol("__afl_sharedmem_fuzzing");
 Afl.jsApiDone = Afl.jsApiGetFunction("js_api_done", "void", []);
 Afl.jsApiError = Afl.jsApiGetFunction("js_api_error", "void", ["pointer"]);
+Afl.jsApiSetBackpatchDisable = Afl.jsApiGetFunction("js_api_set_backpatch_disable", "void", []);
 Afl.jsApiSetDebugMaps = Afl.jsApiGetFunction("js_api_set_debug_maps", "void", []);
 Afl.jsApiSetEntryPoint = Afl.jsApiGetFunction("js_api_set_entrypoint", "void", ["pointer"]);
 Afl.jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_coverage_file", "void", ["pointer"]);
@@ -272,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", []);
@@ -280,12 +307,14 @@ 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.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"]);
 Afl.jsApiSetStdErr = Afl.jsApiGetFunction("js_api_set_stderr", "void", ["pointer"]);
 Afl.jsApiSetStdOut = Afl.jsApiGetFunction("js_api_set_stdout", "void", ["pointer"]);
+Afl.jsApiSetTraceable = Afl.jsApiGetFunction("js_api_set_traceable", "void", []);
 Afl.jsApiWrite = new NativeFunction(
 /* tslint:disable-next-line:no-null-keyword */
 Module.getExportByName(null, "write"), "int", ["int", "pointer", "int"]);
diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c
index e3cd4933..5f477388 100644
--- a/frida_mode/src/js/js.c
+++ b/frida_mode/src/js/js.c
@@ -1,14 +1,13 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #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;
@@ -25,7 +24,7 @@ static void js_msg(GumScript *script, const gchar *message, GBytes *data,
   UNUSED_PARAMETER(script);
   UNUSED_PARAMETER(data);
   UNUSED_PARAMETER(user_data);
-  OKF("%s", message);
+  FOKF("%s", message);
 
 }
 
@@ -50,14 +49,14 @@ static gchar *js_get_script() {
 
     } else {
 
-      FATAL("Could not load script file: %s", filename);
+      FFATAL("Could not load script file: %s", filename);
 
     }
 
   } else {
 
-    OKF("Loaded AFL script: %s, %" G_GSIZE_MODIFIER "d bytes", filename,
-        length);
+    FOKF("Loaded AFL script: %s, %" G_GSIZE_MODIFIER "d bytes", filename,
+         length);
 
     gchar *source = g_malloc0(api_js_len + length + 1);
     memcpy(source, api_js, api_js_len);
@@ -75,7 +74,7 @@ static void js_print_script(gchar *source) {
 
   for (size_t i = 0; split[i] != NULL; i++) {
 
-    OKF("%3" G_GSIZE_MODIFIER "d. %s", i + 1, split[i]);
+    FOKF("%3" G_GSIZE_MODIFIER "d. %s", i + 1, split[i]);
 
   }
 
@@ -89,7 +88,7 @@ static void load_cb(GObject *source_object, GAsyncResult *result,
   UNUSED_PARAMETER(source_object);
   UNUSED_PARAMETER(user_data);
   gum_script_load_finish(script, result);
-  if (error != NULL) { FATAL("Failed to load script - %s", error->message); }
+  if (error != NULL) { FFATAL("Failed to load script - %s", error->message); }
 
 }
 
@@ -99,7 +98,7 @@ static void create_cb(GObject *source_object, GAsyncResult *result,
   UNUSED_PARAMETER(source_object);
   UNUSED_PARAMETER(user_data);
   script = gum_script_backend_create_finish(backend, result, &error);
-  if (error != NULL) { FATAL("Failed to create script: %s", error->message); }
+  if (error != NULL) { FFATAL("Failed to create script: %s", error->message); }
 
   gum_script_set_message_handler(script, js_msg, NULL, NULL);
 
@@ -128,7 +127,7 @@ void js_start(void) {
   while (g_main_context_pending(context))
     g_main_context_iteration(context, FALSE);
 
-  if (!js_done) { FATAL("Script didn't call Afl.done()"); }
+  if (!js_done) { FFATAL("Script didn't call Afl.done()"); }
 
 }
 
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 9dba79aa..5021b531 100644
--- a/frida_mode/src/js/js_api.c
+++ b/frida_mode/src/js/js_api.c
@@ -1,4 +1,3 @@
-#include "debug.h"
 
 #include "entry.h"
 #include "instrument.h"
@@ -12,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;
@@ -20,7 +23,7 @@ __attribute__((visibility("default"))) void js_api_done() {
 
 __attribute__((visibility("default"))) void js_api_error(char *msg) {
 
-  FATAL("%s", msg);
+  FFATAL("%s", msg);
 
 }
 
@@ -48,6 +51,8 @@ __attribute__((visibility("default"))) void js_api_set_persistent_address(
 
   persistent_start = GPOINTER_TO_SIZE(address);
 
+  __afl_set_persistent_mode(1);
+
 }
 
 __attribute__((visibility("default"))) void js_api_set_persistent_return(
@@ -231,3 +236,29 @@ __attribute__((visibility("default"))) void js_api_set_stalker_ic_entries(
 
 }
 
+__attribute__((visibility("default"))) void js_api_set_traceable(void) {
+
+  traceable = TRUE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_backpatch_disable(void) {
+
+  backpatch_enable = FALSE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_stalker_adjacent_blocks(
+    guint val) {
+
+  stalker_adjacent_blocks = val;
+
+}
+
+__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/lib/lib.c b/frida_mode/src/lib/lib.c
index 59a3fcf9..48d2ea2a 100644
--- a/frida_mode/src/lib/lib.c
+++ b/frida_mode/src/lib/lib.c
@@ -8,9 +8,8 @@
 
   #include "frida-gumjs.h"
 
-  #include "debug.h"
-
   #include "lib.h"
+  #include "util.h"
 
   #if defined(__arm__) || defined(__i386__)
     #define ELFCLASS ELFCLASS32
@@ -55,11 +54,11 @@ static gboolean lib_find_exe(const GumModuleDetails *details,
 
 static void lib_validate_hdr(Elf_Ehdr *hdr) {
 
-  if (hdr->e_ident[0] != ELFMAG0) FATAL("Invalid e_ident[0]");
-  if (hdr->e_ident[1] != ELFMAG1) FATAL("Invalid e_ident[1]");
-  if (hdr->e_ident[2] != ELFMAG2) FATAL("Invalid e_ident[2]");
-  if (hdr->e_ident[3] != ELFMAG3) FATAL("Invalid e_ident[3]");
-  if (hdr->e_ident[4] != ELFCLASS) FATAL("Invalid class");
+  if (hdr->e_ident[0] != ELFMAG0) FFATAL("Invalid e_ident[0]");
+  if (hdr->e_ident[1] != ELFMAG1) FFATAL("Invalid e_ident[1]");
+  if (hdr->e_ident[2] != ELFMAG2) FFATAL("Invalid e_ident[2]");
+  if (hdr->e_ident[3] != ELFMAG3) FFATAL("Invalid e_ident[3]");
+  if (hdr->e_ident[4] != ELFCLASS) FFATAL("Invalid class");
 
 }
 
@@ -88,18 +87,22 @@ static void lib_read_text_section(lib_details_t *lib_details, Elf_Ehdr *hdr) {
 
   }
 
-  if (!found_preferred_base) { FATAL("Failed to find preferred load address"); }
+  if (!found_preferred_base) {
+
+    FFATAL("Failed to find preferred load address");
+
+  }
 
-  OKF("Image preferred load address 0x%016" G_GSIZE_MODIFIER "x",
-      preferred_base);
+  FOKF("Image preferred load address 0x%016" G_GSIZE_MODIFIER "x",
+       preferred_base);
 
   shdr = (Elf_Shdr *)((char *)hdr + hdr->e_shoff);
   shstrtab = &shdr[hdr->e_shstrndx];
   shstr = (char *)hdr + shstrtab->sh_offset;
 
-  OKF("shdr: %p", shdr);
-  OKF("shstrtab: %p", shstrtab);
-  OKF("shstr: %p", shstr);
+  FOKF("shdr: %p", shdr);
+  FOKF("shstrtab: %p", shstrtab);
+  FOKF("shstr: %p", shstr);
 
   for (size_t i = 0; i < hdr->e_shnum; i++) {
 
@@ -108,16 +111,16 @@ static void lib_read_text_section(lib_details_t *lib_details, Elf_Ehdr *hdr) {
     if (curr->sh_name == 0) continue;
 
     section_name = &shstr[curr->sh_name];
-    OKF("Section: %2" G_GSIZE_MODIFIER "u - base: 0x%016" G_GSIZE_MODIFIER
-        "X size: 0x%016" G_GSIZE_MODIFIER "X %s",
-        i, curr->sh_addr, curr->sh_size, section_name);
+    FOKF("Section: %2" G_GSIZE_MODIFIER "u - base: 0x%016" G_GSIZE_MODIFIER
+         "X size: 0x%016" G_GSIZE_MODIFIER "X %s",
+         i, curr->sh_addr, curr->sh_size, section_name);
     if (memcmp(section_name, text_name, sizeof(text_name)) == 0 &&
         text_base == 0) {
 
       text_base = lib_details->base_address + curr->sh_addr - preferred_base;
       text_limit = text_base + curr->sh_size;
-      OKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base);
-      OKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit);
+      FOKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base);
+      FOKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit);
 
     }
 
@@ -132,16 +135,16 @@ static void lib_get_text_section(lib_details_t *details) {
   Elf_Ehdr *hdr;
 
   fd = open(details->path, O_RDONLY);
-  if (fd < 0) { FATAL("Failed to open %s", details->path); }
+  if (fd < 0) { FFATAL("Failed to open %s", details->path); }
 
   len = lseek(fd, 0, SEEK_END);
 
-  if (len == (off_t)-1) { FATAL("Failed to lseek %s", details->path); }
+  if (len == (off_t)-1) { FFATAL("Failed to lseek %s", details->path); }
 
-  OKF("len: %ld", len);
+  FOKF("len: %ld", len);
 
   hdr = (Elf_Ehdr *)mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
-  if (hdr == MAP_FAILED) { FATAL("Failed to map %s", details->path); }
+  if (hdr == MAP_FAILED) { FFATAL("Failed to map %s", details->path); }
 
   lib_validate_hdr(hdr);
   lib_read_text_section(details, hdr);
@@ -159,22 +162,22 @@ void lib_init(void) {
 
   lib_details_t lib_details;
   gum_process_enumerate_modules(lib_find_exe, &lib_details);
-  OKF("Executable: 0x%016" G_GINT64_MODIFIER "x - %s", lib_details.base_address,
-      lib_details.path);
+  FOKF("Executable: 0x%016" G_GINT64_MODIFIER "x - %s",
+       lib_details.base_address, lib_details.path);
   lib_get_text_section(&lib_details);
 
 }
 
 guint64 lib_get_text_base(void) {
 
-  if (text_base == 0) FATAL("Lib not initialized");
+  if (text_base == 0) FFATAL("Lib not initialized");
   return text_base;
 
 }
 
 guint64 lib_get_text_limit(void) {
 
-  if (text_limit == 0) FATAL("Lib not initialized");
+  if (text_limit == 0) FFATAL("Lib not initialized");
   return text_limit;
 
 }
diff --git a/frida_mode/src/lib/lib_apple.c b/frida_mode/src/lib/lib_apple.c
index 2aa48a13..3bdb8c10 100644
--- a/frida_mode/src/lib/lib_apple.c
+++ b/frida_mode/src/lib/lib_apple.c
@@ -1,8 +1,6 @@
 #ifdef __APPLE__
   #include "frida-gumjs.h"
 
-  #include "debug.h"
-
   #include "lib.h"
   #include "util.h"
 
@@ -22,7 +20,7 @@ static gboolean lib_get_main_module(const GumModuleDetails *details,
       details->path, mach_task_self(), details->range->base_address,
       GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
 
-  OKF("Found main module: %s", module->name);
+  FOKF("Found main module: %s", module->name);
 
   *ret = module;
 
@@ -37,18 +35,18 @@ gboolean lib_get_text_section(const GumDarwinSectionDetails *details,
   static size_t idx = 0;
   char          text_name[] = "__text";
 
-  OKF("Section: %2lu - base: 0x%016" G_GINT64_MODIFIER
-      "X size: 0x%016" G_GINT64_MODIFIER "X %s",
-      idx++, details->vm_address, details->vm_address + details->size,
-      details->section_name);
+  FOKF("Section: %2lu - base: 0x%016" G_GINT64_MODIFIER
+       "X size: 0x%016" G_GINT64_MODIFIER "X %s",
+       idx++, details->vm_address, details->vm_address + details->size,
+       details->section_name);
 
   if (memcmp(details->section_name, text_name, sizeof(text_name)) == 0 &&
       text_base == 0) {
 
     text_base = details->vm_address;
     text_limit = details->vm_address + details->size;
-    OKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base);
-    OKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit);
+    FOKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base);
+    FOKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit);
 
   }
 
@@ -70,14 +68,14 @@ void lib_init(void) {
 
 guint64 lib_get_text_base(void) {
 
-  if (text_base == 0) FATAL("Lib not initialized");
+  if (text_base == 0) FFATAL("Lib not initialized");
   return text_base;
 
 }
 
 guint64 lib_get_text_limit(void) {
 
-  if (text_limit == 0) FATAL("Lib not initialized");
+  if (text_limit == 0) FFATAL("Lib not initialized");
   return text_limit;
 
 }
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index c0de9c6b..1be63bc4 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -6,6 +6,7 @@
 #ifdef __APPLE__
   #include <mach/mach.h>
   #include <mach-o/dyld_images.h>
+  #include <crt_externs.h>
 #else
   #include <sys/wait.h>
   #include <sys/personality.h>
@@ -14,7 +15,6 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 
 #include "entry.h"
 #include "instrument.h"
@@ -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;
 
@@ -62,7 +62,7 @@ static void on_main_os(int argc, char **argv, char **envp) {
   /* Personality doesn't affect the current process, it only takes effect on
    * evec */
   int persona = personality(ADDR_NO_RANDOMIZE);
-  if (persona == -1) { WARNF("Failed to set ADDR_NO_RANDOMIZE: %d", errno); }
+  if (persona == -1) { FWARNF("Failed to set ADDR_NO_RANDOMIZE: %d", errno); }
   if ((persona & ADDR_NO_RANDOMIZE) == 0) { execvpe(argv[0], argv, envp); }
 
   GumInterceptor *interceptor = gum_interceptor_obtain();
@@ -90,13 +90,14 @@ static void embedded_init(void) {
 
 static void afl_print_cmdline(void) {
 
+#if defined(__linux__)
   char * buffer = g_malloc0(PROC_MAX);
   gchar *fname = g_strdup_printf("/proc/%d/cmdline", getppid());
   int    fd = open(fname, O_RDONLY);
 
   if (fd < 0) {
 
-    WARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
+    FWARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
     return;
 
   }
@@ -104,7 +105,7 @@ static void afl_print_cmdline(void) {
   ssize_t bytes_read = read(fd, buffer, PROC_MAX - 1);
   if (bytes_read < 0) {
 
-    FATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno);
+    FFATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno);
 
   }
 
@@ -114,7 +115,7 @@ static void afl_print_cmdline(void) {
 
     if (i == 0 || buffer[i - 1] == '\0') {
 
-      OKF("AFL - COMMANDLINE: argv[%d] = %s", idx++, &buffer[i]);
+      FOKF("AFL - COMMANDLINE: argv[%d] = %s", idx++, &buffer[i]);
 
     }
 
@@ -123,6 +124,18 @@ static void afl_print_cmdline(void) {
   close(fd);
   g_free(fname);
   g_free(buffer);
+#elif defined(__APPLE__)
+  int    idx;
+  char **argv = *_NSGetArgv();
+  int    nargv = *_NSGetArgc();
+
+  for (idx = 0; idx < nargv; idx++) {
+
+    FOKF("AFL - COMMANDLINE: argv[%d] = %s", idx, argv[idx]);
+
+  }
+
+#endif
 
 }
 
@@ -134,7 +147,7 @@ static void afl_print_env(void) {
 
   if (fd < 0) {
 
-    WARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
+    FWARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
     return;
 
   }
@@ -142,7 +155,7 @@ static void afl_print_env(void) {
   ssize_t bytes_read = read(fd, buffer, PROC_MAX - 1);
   if (bytes_read < 0) {
 
-    FATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno);
+    FFATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno);
 
   }
 
@@ -152,7 +165,7 @@ static void afl_print_env(void) {
 
     if (i == 0 || buffer[i - 1] == '\0') {
 
-      OKF("AFL - ENVIRONMENT %3d: %s", idx++, &buffer[i]);
+      FOKF("AFL - ENVIRONMENT %3d: %s", idx++, &buffer[i]);
 
     }
 
@@ -204,7 +217,9 @@ __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) {
+
+  int ret;
 
   on_main_os(argc, argv, envp);
 
@@ -212,12 +227,22 @@ static int *on_main(int argc, char **argv, char **envp) {
 
   afl_frida_start();
 
-  return main_fn(argc, argv, envp);
+  if (js_main_hook != NULL) {
+
+    ret = js_main_hook(argc, argv, envp);
+
+  } else {
+
+    ret = main_fn(argc, argv, envp);
+
+  }
+
+  return ret;
 
 }
 
 #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) {
 
@@ -230,9 +255,9 @@ static void intercept_main(void) {
 static void intercept_main(void) {
 
   mach_port_t task = mach_task_self();
-  OKF("Task Id: %u", task);
+  FOKF("Task Id: %u", task);
   GumAddress entry = gum_darwin_find_entrypoint(task);
-  OKF("Entry Point: 0x%016" G_GINT64_MODIFIER "x", entry);
+  FOKF("Entry Point: 0x%016" G_GINT64_MODIFIER "x", entry);
   void *main = GSIZE_TO_POINTER(entry);
   main_fn = main;
   intercept_hook(main, on_main, NULL);
@@ -240,7 +265,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/output.c b/frida_mode/src/output.c
index e2b744e7..f570fe91 100644
--- a/frida_mode/src/output.c
+++ b/frida_mode/src/output.c
@@ -4,9 +4,8 @@
 
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "output.h"
+#include "util.h"
 
 char *output_stdout = NULL;
 char *output_stderr = NULL;
@@ -19,18 +18,18 @@ static void output_redirect(int fd, char *filename) {
 
   path = g_canonicalize_filename(filename, g_get_current_dir());
 
-  OKF("Redirect %d -> '%s'", fd, path);
+  FOKF("Redirect %d -> '%s'", fd, path);
 
   int output_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
                        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 
   g_free(path);
 
-  if (output_fd < 0) { FATAL("Failed to open fd(%d) error %d", fd, errno); }
+  if (output_fd < 0) { FFATAL("Failed to open fd(%d) error %d", fd, errno); }
 
   if (dup2(output_fd, fd) < 0) {
 
-    FATAL("Failed to set fd(%d) error %d", fd, errno);
+    FFATAL("Failed to set fd(%d) error %d", fd, errno);
 
   }
 
@@ -47,8 +46,8 @@ void output_config(void) {
 
 void output_init(void) {
 
-  OKF("Output - StdOut: %s", output_stdout);
-  OKF("Output - StdErr: %s", output_stderr);
+  FOKF("Output - StdOut: %s", output_stdout);
+  FOKF("Output - StdErr: %s", output_stderr);
 
   output_redirect(STDOUT_FILENO, output_stdout);
   output_redirect(STDERR_FILENO, output_stderr);
diff --git a/frida_mode/src/persistent/persistent.c b/frida_mode/src/persistent/persistent.c
index b2915a2f..817d9925 100644
--- a/frida_mode/src/persistent/persistent.c
+++ b/frida_mode/src/persistent/persistent.c
@@ -3,7 +3,6 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 
 #include "entry.h"
 #include "persistent.h"
@@ -23,15 +22,15 @@ gboolean               persistent_debug = FALSE;
 void persistent_config(void) {
 
   hook_name = getenv("AFL_FRIDA_PERSISTENT_HOOK");
-  persistent_start = util_read_address("AFL_FRIDA_PERSISTENT_ADDR");
-  persistent_count = util_read_num("AFL_FRIDA_PERSISTENT_CNT");
-  persistent_ret = util_read_address("AFL_FRIDA_PERSISTENT_RET");
+  persistent_start = util_read_address("AFL_FRIDA_PERSISTENT_ADDR", 0);
+  persistent_count = util_read_num("AFL_FRIDA_PERSISTENT_CNT", 0);
+  persistent_ret = util_read_address("AFL_FRIDA_PERSISTENT_RET", 0);
 
   if (getenv("AFL_FRIDA_PERSISTENT_DEBUG") != NULL) { persistent_debug = TRUE; }
 
   if (persistent_count != 0 && persistent_start == 0) {
 
-    FATAL(
+    FFATAL(
         "AFL_FRIDA_PERSISTENT_ADDR must be specified if "
         "AFL_FRIDA_PERSISTENT_CNT is");
 
@@ -40,11 +39,11 @@ void persistent_config(void) {
   if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000;
 
   if (persistent_start != 0 && !persistent_is_supported())
-    FATAL("Persistent mode not supported on this architecture");
+    FFATAL("Persistent mode not supported on this architecture");
 
   if (persistent_ret != 0 && persistent_start == 0) {
 
-    FATAL(
+    FFATAL(
         "AFL_FRIDA_PERSISTENT_ADDR must be specified if "
         "AFL_FRIDA_PERSISTENT_RET is");
 
@@ -54,33 +53,33 @@ void persistent_config(void) {
 
   void *hook_obj = dlopen(hook_name, RTLD_NOW);
   if (hook_obj == NULL)
-    FATAL("Failed to load AFL_FRIDA_PERSISTENT_HOOK (%s)", hook_name);
+    FFATAL("Failed to load AFL_FRIDA_PERSISTENT_HOOK (%s)", hook_name);
 
   int (*afl_persistent_hook_init_ptr)(void) =
       dlsym(hook_obj, "afl_persistent_hook_init");
   if (afl_persistent_hook_init_ptr == NULL)
-    FATAL("Failed to find afl_persistent_hook_init in %s", hook_name);
+    FFATAL("Failed to find afl_persistent_hook_init in %s", hook_name);
 
   if (afl_persistent_hook_init_ptr() == 0)
-    FATAL("afl_persistent_hook_init returned a failure");
+    FFATAL("afl_persistent_hook_init returned a failure");
 
   persistent_hook =
       (afl_persistent_hook_fn)dlsym(hook_obj, "afl_persistent_hook");
   if (persistent_hook == NULL)
-    FATAL("Failed to find afl_persistent_hook in %s", hook_name);
+    FFATAL("Failed to find afl_persistent_hook in %s", hook_name);
 
 }
 
 void persistent_init(void) {
 
-  OKF("Instrumentation - persistent mode [%c] (0x%016" G_GINT64_MODIFIER "X)",
-      persistent_start == 0 ? ' ' : 'X', persistent_start);
-  OKF("Instrumentation - persistent count [%c] (%" G_GINT64_MODIFIER "d)",
-      persistent_start == 0 ? ' ' : 'X', persistent_count);
-  OKF("Instrumentation - hook [%s]", hook_name);
+  FOKF("Instrumentation - persistent mode [%c] (0x%016" G_GINT64_MODIFIER "X)",
+       persistent_start == 0 ? ' ' : 'X', persistent_start);
+  FOKF("Instrumentation - persistent count [%c] (%" G_GINT64_MODIFIER "d)",
+       persistent_start == 0 ? ' ' : 'X', persistent_count);
+  FOKF("Instrumentation - hook [%s]", hook_name);
 
-  OKF("Instrumentation - persistent ret [%c] (0x%016" G_GINT64_MODIFIER "X)",
-      persistent_ret == 0 ? ' ' : 'X', persistent_ret);
+  FOKF("Instrumentation - persistent ret [%c] (0x%016" G_GINT64_MODIFIER "X)",
+       persistent_ret == 0 ? ' ' : 'X', persistent_ret);
 
   if (persistent_hook != NULL) { __afl_sharedmem_fuzzing = 1; }
 
@@ -88,7 +87,7 @@ void persistent_init(void) {
 
 void persistent_prologue(GumStalkerOutput *output) {
 
-  OKF("AFL_FRIDA_PERSISTENT_ADDR reached");
+  FOKF("AFL_FRIDA_PERSISTENT_ADDR reached");
   entry_compiled = TRUE;
   ranges_exclude();
   stalker_trust();
@@ -98,7 +97,7 @@ void persistent_prologue(GumStalkerOutput *output) {
 
 void persistent_epilogue(GumStalkerOutput *output) {
 
-  OKF("AFL_FRIDA_PERSISTENT_RET reached");
+  FOKF("AFL_FRIDA_PERSISTENT_RET reached");
   persistent_epilogue_arch(output);
 
 }
diff --git a/frida_mode/src/persistent/persistent_arm32.c b/frida_mode/src/persistent/persistent_arm32.c
index 769f1505..b4e50897 100644
--- a/frida_mode/src/persistent/persistent_arm32.c
+++ b/frida_mode/src/persistent/persistent_arm32.c
@@ -1,7 +1,5 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "persistent.h"
 #include "util.h"
 
@@ -64,14 +62,14 @@ gboolean persistent_is_supported(void) {
 void persistent_prologue_arch(GumStalkerOutput *output) {
 
   UNUSED_PARAMETER(output);
-  FATAL("Persistent mode not supported on this architecture");
+  FFATAL("Persistent mode not supported on this architecture");
 
 }
 
 void persistent_epilogue_arch(GumStalkerOutput *output) {
 
   UNUSED_PARAMETER(output);
-  FATAL("Persistent mode not supported on this architecture");
+  FFATAL("Persistent mode not supported on this architecture");
 
 }
 
diff --git a/frida_mode/src/persistent/persistent_arm64.c b/frida_mode/src/persistent/persistent_arm64.c
index 3cd61cd5..c9159ca1 100644
--- a/frida_mode/src/persistent/persistent_arm64.c
+++ b/frida_mode/src/persistent/persistent_arm64.c
@@ -2,7 +2,6 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 
 #include "instrument.h"
 #include "persistent.h"
@@ -325,7 +324,7 @@ void persistent_prologue_arch(GumStalkerOutput *output) {
 
   gconstpointer loop = cw->code + 1;
 
-  OKF("Persistent loop reached");
+  FOKF("Persistent loop reached");
 
   instrument_persitent_save_regs(cw, &saved_regs);
 
diff --git a/frida_mode/src/persistent/persistent_x64.c b/frida_mode/src/persistent/persistent_x64.c
index c0bd9a09..8cbde633 100644
--- a/frida_mode/src/persistent/persistent_x64.c
+++ b/frida_mode/src/persistent/persistent_x64.c
@@ -2,7 +2,6 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 
 #include "instrument.h"
 #include "persistent.h"
@@ -270,7 +269,7 @@ void persistent_prologue_arch(GumStalkerOutput *output) {
 
   gconstpointer loop = cw->code + 1;
 
-  OKF("Persistent loop reached");
+  FOKF("Persistent loop reached");
 
   /* Pop the return value */
   gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 8);
diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c
index b911676a..5425b01b 100644
--- a/frida_mode/src/persistent/persistent_x86.c
+++ b/frida_mode/src/persistent/persistent_x86.c
@@ -1,10 +1,10 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 
 #include "instrument.h"
 #include "persistent.h"
+#include "util.h"
 
 #if defined(__i386__)
 
@@ -210,7 +210,7 @@ void persistent_prologue_arch(GumStalkerOutput *output) {
 
   gconstpointer loop = cw->code + 1;
 
-  OKF("Persistent loop reached");
+  FOKF("Persistent loop reached");
 
   /* Pop the return value */
   gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 4);
diff --git a/frida_mode/src/prefetch.c b/frida_mode/src/prefetch.c
index 0efbc9bf..8c9ce94d 100644
--- a/frida_mode/src/prefetch.c
+++ b/frida_mode/src/prefetch.c
@@ -4,8 +4,6 @@
 
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "entry.h"
 #include "intercept.h"
 #include "prefetch.h"
@@ -44,8 +42,9 @@ static void gum_afl_stalker_backpatcher_notify(GumStalkerObserver *self,
       sizeof(prefetch_data->backpatch_data) - prefetch_data->backpatch_size;
   if (sizeof(gsize) + size > remaining) { return; }
 
-  *(gsize *)(&prefetch_data->backpatch_data[prefetch_data->backpatch_size]) =
-      size;
+  gsize *dst_backpatch_size =
+      (gsize *)&prefetch_data->backpatch_data[prefetch_data->backpatch_size];
+  *dst_backpatch_size = size;
   prefetch_data->backpatch_size += sizeof(gsize);
 
   memcpy(&prefetch_data->backpatch_data[prefetch_data->backpatch_size],
@@ -115,12 +114,13 @@ static void prefetch_read_patches(void) {
        remaining > sizeof(gsize);
        remaining = prefetch_data->backpatch_size - offset) {
 
-    gsize size = *(gsize *)(&prefetch_data->backpatch_data[offset]);
+    gsize *src_backpatch_data = (gsize *)&prefetch_data->backpatch_data[offset];
+    gsize  size = *src_backpatch_data;
     offset += sizeof(gsize);
 
     if (prefetch_data->backpatch_size - offset < size) {
 
-      FATAL("Incomplete backpatch entry");
+      FFATAL("Incomplete backpatch entry");
 
     }
 
@@ -178,9 +178,9 @@ static void prefetch_hook_fork(void) {
 
 void prefetch_init(void) {
 
-  OKF("Instrumentation - prefetch [%c]", prefetch_enable ? 'X' : ' ');
-  OKF("Instrumentation - prefetch_backpatch [%c]",
-      prefetch_backpatch ? 'X' : ' ');
+  FOKF("Instrumentation - prefetch [%c]", prefetch_enable ? 'X' : ' ');
+  FOKF("Instrumentation - prefetch_backpatch [%c]",
+       prefetch_backpatch ? 'X' : ' ');
 
   if (!prefetch_enable) { return; }
   /*
@@ -192,7 +192,7 @@ void prefetch_init(void) {
       shmget(IPC_PRIVATE, sizeof(prefetch_data_t), IPC_CREAT | IPC_EXCL | 0600);
   if (prefetch_shm_id < 0) {
 
-    FATAL("prefetch_shm_id < 0 - errno: %d\n", errno);
+    FFATAL("prefetch_shm_id < 0 - errno: %d\n", errno);
 
   }
 
@@ -204,7 +204,7 @@ void prefetch_init(void) {
    */
   if (shmctl(prefetch_shm_id, IPC_RMID, NULL) < 0) {
 
-    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+    FFATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
 
   }
 
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
index 5b6eb462..9844c74c 100644
--- a/frida_mode/src/ranges.c
+++ b/frida_mode/src/ranges.c
@@ -1,7 +1,5 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "lib.h"
 #include "ranges.h"
 #include "stalker.h"
@@ -37,8 +35,8 @@ static void convert_address_token(gchar *token, GumMemoryRange *range) {
 
   if (token_count != 2) {
 
-    FATAL("Invalid range (should have two addresses seperated by a '-'): %s\n",
-          token);
+    FFATAL("Invalid range (should have two addresses seperated by a '-'): %s\n",
+           token);
 
   }
 
@@ -47,15 +45,15 @@ static void convert_address_token(gchar *token, GumMemoryRange *range) {
 
   if (!g_str_has_prefix(from_str, "0x")) {
 
-    FATAL("Invalid range: %s - Start address should have 0x prefix: %s\n",
-          token, from_str);
+    FFATAL("Invalid range: %s - Start address should have 0x prefix: %s\n",
+           token, from_str);
 
   }
 
   if (!g_str_has_prefix(to_str, "0x")) {
 
-    FATAL("Invalid range: %s - End address should have 0x prefix: %s\n", token,
-          to_str);
+    FFATAL("Invalid range: %s - End address should have 0x prefix: %s\n", token,
+           to_str);
 
   }
 
@@ -66,8 +64,8 @@ static void convert_address_token(gchar *token, GumMemoryRange *range) {
 
     if (!g_ascii_isxdigit(*c)) {
 
-      FATAL("Invalid range: %s - Start address not formed of hex digits: %s\n",
-            token, from_str);
+      FFATAL("Invalid range: %s - Start address not formed of hex digits: %s\n",
+             token, from_str);
 
     }
 
@@ -77,8 +75,8 @@ static void convert_address_token(gchar *token, GumMemoryRange *range) {
 
     if (!g_ascii_isxdigit(*c)) {
 
-      FATAL("Invalid range: %s - End address not formed of hex digits: %s\n",
-            token, to_str);
+      FFATAL("Invalid range: %s - End address not formed of hex digits: %s\n",
+             token, to_str);
 
     }
 
@@ -87,24 +85,25 @@ static void convert_address_token(gchar *token, GumMemoryRange *range) {
   guint64 from = g_ascii_strtoull(from_str, NULL, 16);
   if (from == 0) {
 
-    FATAL("Invalid range: %s - Start failed hex conversion: %s\n", token,
-          from_str);
+    FFATAL("Invalid range: %s - Start failed hex conversion: %s\n", token,
+           from_str);
 
   }
 
   guint64 to = g_ascii_strtoull(to_str, NULL, 16);
   if (to == 0) {
 
-    FATAL("Invalid range: %s - End failed hex conversion: %s\n", token, to_str);
+    FFATAL("Invalid range: %s - End failed hex conversion: %s\n", token,
+           to_str);
 
   }
 
   if (from >= to) {
 
-    FATAL("Invalid range: %s - Start (0x%016" G_GINT64_MODIFIER
-          "x) must be less than end "
-          "(0x%016" G_GINT64_MODIFIER "x)\n",
-          token, from, to);
+    FFATAL("Invalid range: %s - Start (0x%016" G_GINT64_MODIFIER
+           "x) must be less than end "
+           "(0x%016" G_GINT64_MODIFIER "x)\n",
+           token, from, to);
 
   }
 
@@ -123,10 +122,10 @@ static gboolean convert_name_token_for_module(const GumModuleDetails *details,
 
   if (!g_str_has_suffix(details->path, ctx->suffix)) { return true; };
 
-  OKF("Found module - prefix: %s, 0x%016" G_GINT64_MODIFIER
-      "x-0x%016" G_GINT64_MODIFIER "x %s",
-      ctx->suffix, details->range->base_address,
-      details->range->base_address + details->range->size, details->path);
+  FOKF("Found module - prefix: %s, 0x%016" G_GINT64_MODIFIER
+       "x-0x%016" G_GINT64_MODIFIER "x %s",
+       ctx->suffix, details->range->base_address,
+       details->range->base_address + details->range->size, details->path);
 
   *ctx->range = *details->range;
   ctx->done = true;
@@ -140,7 +139,7 @@ static void convert_name_token(gchar *token, GumMemoryRange *range) {
   convert_name_ctx_t ctx = {.suffix = suffix, .range = range, .done = false};
 
   gum_process_enumerate_modules(convert_name_token_for_module, &ctx);
-  if (!ctx.done) { FATAL("Failed to resolve module: %s\n", token); }
+  if (!ctx.done) { FFATAL("Failed to resolve module: %s\n", token); }
   g_free(suffix);
 
 }
@@ -159,16 +158,30 @@ static void convert_token(gchar *token, GumMemoryRange *range) {
 
   }
 
-  OKF("Converted token: %s -> 0x%016" G_GINT64_MODIFIER
-      "x-0x%016" G_GINT64_MODIFIER "x\n",
-      token, range->base_address, range->base_address + range->size);
+  FOKF("Converted token: %s -> 0x%016" G_GINT64_MODIFIER
+       "x-0x%016" G_GINT64_MODIFIER "x\n",
+       token, range->base_address, range->base_address + range->size);
 
 }
 
 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;
+
+  }
 
 }
 
@@ -179,24 +192,24 @@ static gboolean print_ranges_callback(const GumRangeDetails *details,
 
   if (details->file == NULL) {
 
-    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
-        "X %c%c%c",
-        details->range->base_address,
-        details->range->base_address + details->range->size,
-        details->protection & GUM_PAGE_READ ? 'R' : '-',
-        details->protection & GUM_PAGE_WRITE ? 'W' : '-',
-        details->protection & GUM_PAGE_EXECUTE ? 'X' : '-');
+    FOKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
+         "X %c%c%c",
+         details->range->base_address,
+         details->range->base_address + details->range->size,
+         details->protection & GUM_PAGE_READ ? 'R' : '-',
+         details->protection & GUM_PAGE_WRITE ? 'W' : '-',
+         details->protection & GUM_PAGE_EXECUTE ? 'X' : '-');
 
   } else {
 
-    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
-        "X %c%c%c %s(0x%016" G_GINT64_MODIFIER "x)",
-        details->range->base_address,
-        details->range->base_address + details->range->size,
-        details->protection & GUM_PAGE_READ ? 'R' : '-',
-        details->protection & GUM_PAGE_WRITE ? 'W' : '-',
-        details->protection & GUM_PAGE_EXECUTE ? 'X' : '-', details->file->path,
-        details->file->offset);
+    FOKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
+         "X %c%c%c %s(0x%016" G_GINT64_MODIFIER "x)",
+         details->range->base_address,
+         details->range->base_address + details->range->size,
+         details->protection & GUM_PAGE_READ ? 'R' : '-',
+         details->protection & GUM_PAGE_WRITE ? 'W' : '-',
+         details->protection & GUM_PAGE_EXECUTE ? 'X' : '-',
+         details->file->path, details->file->offset);
 
   }
 
@@ -206,14 +219,14 @@ static gboolean print_ranges_callback(const GumRangeDetails *details,
 
 static void print_ranges(char *key, GArray *ranges) {
 
-  OKF("Range: %s Length: %d", key, ranges->len);
+  FOKF("Range: %s Length: %d", key, ranges->len);
   for (guint i = 0; i < ranges->len; i++) {
 
     GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
     GumAddress      curr_limit = curr->base_address + curr->size;
-    OKF("Range: %s Idx: %3d - 0x%016" G_GINT64_MODIFIER
-        "x-0x%016" G_GINT64_MODIFIER "x",
-        key, i, curr->base_address, curr_limit);
+    FOKF("Range: %s Idx: %3d - 0x%016" G_GINT64_MODIFIER
+         "x-0x%016" G_GINT64_MODIFIER "x",
+         key, i, curr->base_address, curr_limit);
 
   }
 
@@ -250,10 +263,10 @@ static void check_for_overlaps(GArray *array) {
     GumAddress      curr_limit = curr->base_address + curr->size;
     if (prev_limit > curr->base_address) {
 
-      FATAL("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);
+      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);
 
     }
 
@@ -549,18 +562,19 @@ static GArray *merge_ranges(GArray *a) {
 
 }
 
+void ranges_print_debug_maps(void) {
+
+  gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback, NULL);
+
+}
+
 void ranges_config(void) {
 
   if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = TRUE; }
   if (getenv("AFL_INST_LIBS") != NULL) { ranges_inst_libs = TRUE; }
   if (getenv("AFL_FRIDA_INST_JIT") != NULL) { ranges_inst_jit = TRUE; }
 
-  if (ranges_debug_maps) {
-
-    gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
-                                 NULL);
-
-  }
+  if (ranges_debug_maps) { ranges_print_debug_maps(); }
 
   include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
   exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
@@ -576,13 +590,13 @@ void ranges_init(void) {
   GArray *       step4;
   GArray *       step5;
 
-  OKF("Ranges - Instrument jit [%c]", ranges_inst_jit ? 'X' : ' ');
-  OKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
+  FOKF("Ranges - Instrument jit [%c]", ranges_inst_jit ? 'X' : ' ');
+  FOKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
 
   print_ranges("AFL_FRIDA_INST_RANGES", include_ranges);
   print_ranges("AFL_FRIDA_EXCLUDE_RANGES", exclude_ranges);
 
-  OKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
+  FOKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
 
   print_ranges("AFL_FRIDA_INST_RANGES", include_ranges);
   print_ranges("AFL_FRIDA_EXCLUDE_RANGES", exclude_ranges);
@@ -659,7 +673,7 @@ void ranges_exclude() {
   GumMemoryRange *r;
   GumStalker *    stalker = stalker_get();
 
-  OKF("Excluding ranges");
+  FOKF("Excluding ranges");
 
   for (guint i = 0; i < ranges->len; i++) {
 
diff --git a/frida_mode/src/seccomp/seccomp.c b/frida_mode/src/seccomp/seccomp.c
index 7683cd71..9d8fdd5d 100644
--- a/frida_mode/src/seccomp/seccomp.c
+++ b/frida_mode/src/seccomp/seccomp.c
@@ -1,123 +1,19 @@
-#include <execinfo.h>
-#include <fcntl.h>
-#include <linux/seccomp.h>
-#include <stdio.h>
-#include <unistd.h>
-
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "seccomp.h"
 #include "util.h"
 
 char *seccomp_filename = NULL;
 
-static void seccomp_vprint(int fd, char *format, va_list ap) {
-
-  char buffer[4096] = {0};
-  int  len;
-
-  if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; }
-
-  len = strnlen(buffer, sizeof(buffer));
-  IGNORED_RETURN(write(fd, buffer, len));
-
-}
-
-void seccomp_print(char *format, ...) {
-
-  va_list ap;
-  va_start(ap, format);
-  seccomp_vprint(SECCOMP_OUTPUT_FILE_FD, format, ap);
-  va_end(ap);
-
-}
-
-static void seccomp_filter_callback(struct seccomp_notif *     req,
-                                    struct seccomp_notif_resp *resp,
-                                    GumReturnAddressArray *    frames) {
-
-  GumDebugSymbolDetails details = {0};
-  if (req->data.nr == SYS_OPENAT) {
-
-    seccomp_print("SYS_OPENAT: (%s)\n", (char *)req->data.args[1]);
-
-  }
-
-  seccomp_print(
-      "\nID (%#llx) for PID %d - %d (%s) [0x%llx 0x%llx 0x%llx 0x%llx 0x%llx "
-      "0x%llx ]\n",
-      req->id, req->pid, req->data.nr, seccomp_syscall_lookup(req->data.nr),
-      req->data.args[0], req->data.args[1], req->data.args[2],
-      req->data.args[3], req->data.args[4], req->data.args[5]);
-
-  seccomp_print("FRAMES: (%u)\n", frames->len);
-  char **syms = backtrace_symbols(frames->items, frames->len);
-  if (syms == NULL) { FATAL("Failed to get symbols"); }
-
-  for (guint i = 0; i < frames->len; i++) {
-
-    if (gum_symbol_details_from_address(frames->items[i], &details)) {
-
-      seccomp_print("\t%3d. %s!%s\n", i, details.module_name,
-                    details.symbol_name);
-
-    } else {
-
-      seccomp_print("\t%3d. %s\n", i, syms[i]);
-
-    }
-
-  }
-
-  free(syms);
-
-  resp->error = 0;
-  resp->val = 0;
-  resp->id = req->id;
-  resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
-
-}
-
-static void seccomp_child(int signal_parent, void *ctx) {
-
-  int sock_fd = *((int *)ctx);
-  int fd = seccomp_socket_recv(sock_fd);
-
-  if (close(sock_fd) < 0) { FATAL("child - close"); }
-
-  seccomp_event_signal(signal_parent);
-  seccomp_filter_child_install();
-  seccomp_filter_run(fd, seccomp_filter_callback);
-
-}
-
 void seccomp_on_fork(void) {
 
-  int   sock[2] = {-1, -1};
-  pid_t child = -1;
-  int   child_fd = -1;
-
   if (seccomp_filename == NULL) { return; }
 
-  seccomp_socket_create(sock);
-  seccomp_child_run(seccomp_child, sock, &child, &child_fd);
-
-  if (dup2(child_fd, SECCOMP_PARENT_EVENT_FD) < 0) { FATAL("dup2"); }
-
-  if (close(child_fd) < 0) { FATAL("seccomp_on_fork - close (1)"); }
-
-  if (close(sock[STDIN_FILENO]) < 0) { FATAL("grandparent - close (2)"); }
-
-  int fd = seccomp_filter_install(child);
-  seccomp_socket_send(sock[STDOUT_FILENO], fd);
-
-  if (close(sock[STDOUT_FILENO]) < 0) { FATAL("grandparent - close (3)"); }
-
-  if (close(fd) < 0) { FATAL("grandparent - close (4)"); }
-
-  seccomp_child_wait(SECCOMP_PARENT_EVENT_FD);
+#ifdef __APPLE__
+  FFATAL("Seccomp not supported on OSX");
+#else
+  seccomp_callback_parent();
+#endif
 
 }
 
@@ -129,29 +25,15 @@ void seccomp_config(void) {
 
 void seccomp_init(void) {
 
-  char *path = NULL;
-  int   fd;
-
-  OKF("Seccomp - file [%s]", seccomp_filename);
+  FOKF("Seccomp - file [%s]", seccomp_filename);
 
   if (seccomp_filename == NULL) { return; }
 
-  path = g_canonicalize_filename(seccomp_filename, g_get_current_dir());
-
-  OKF("Seccomp - path [%s]", path);
-
-  fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
-            S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
-
-  if (dup2(fd, SECCOMP_OUTPUT_FILE_FD) < 0) {
-
-    FATAL("Failed to duplicate seccomp output file");
-
-  }
-
-  if (close(fd) < 0) { FATAL("Failed to close seccomp output file fd"); }
-
-  g_free(path);
+#ifdef __APPLE__
+  FFATAL("Seccomp not supported on OSX");
+#else
+  seccomp_callback_initialize();
+#endif
 
 }
 
diff --git a/frida_mode/src/seccomp/seccomp_atomic.c b/frida_mode/src/seccomp/seccomp_atomic.c
index 1720a726..18cb6724 100644
--- a/frida_mode/src/seccomp/seccomp_atomic.c
+++ b/frida_mode/src/seccomp/seccomp_atomic.c
@@ -1,13 +1,15 @@
-#include <stdbool.h>
-#include <stdio.h>
+#if defined(__linux__) && !defined(__ANDROID__)
 
-#include "debug.h"
+  #include <stdbool.h>
+  #include <stdio.h>
+
+  #include "util.h"
 
 void seccomp_atomic_set(volatile bool *ptr, bool val) {
 
   if (!__sync_bool_compare_and_swap(ptr, !val, val)) {
 
-    FATAL("Failed to set event");
+    FFATAL("Failed to set event");
 
   }
 
@@ -26,3 +28,5 @@ void seccomp_atomic_wait(volatile bool *ptr, bool val) {
 
 }
 
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_callback.c b/frida_mode/src/seccomp/seccomp_callback.c
new file mode 100644
index 00000000..f7aaf78b
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_callback.c
@@ -0,0 +1,145 @@
+#if defined(__linux__) && !defined(__ANDROID__)
+
+  #if !defined(__MUSL__)
+    #include <execinfo.h>
+  #endif
+  #include <fcntl.h>
+
+  #include "seccomp.h"
+  #include "util.h"
+
+static void seccomp_callback_filter(struct seccomp_notif *     req,
+                                    struct seccomp_notif_resp *resp,
+                                    GumReturnAddressArray *    frames) {
+
+  GumDebugSymbolDetails details = {0};
+  if (req->data.nr == SYS_OPENAT) {
+
+  #if UINTPTR_MAX == 0xffffffffffffffffu
+    seccomp_print("SYS_OPENAT: (%s)\n", (char *)req->data.args[1]);
+  #endif
+  #if UINTPTR_MAX == 0xffffffff
+    seccomp_print("SYS_OPENAT: (%s)\n", (char *)(__u32)req->data.args[1]);
+  #endif
+
+  }
+
+  seccomp_print(
+      "\nID (%#llx) for PID %d - %d (%s) [0x%llx 0x%llx 0x%llx 0x%llx 0x%llx "
+      "0x%llx ]\n",
+      req->id, req->pid, req->data.nr, seccomp_syscall_lookup(req->data.nr),
+      req->data.args[0], req->data.args[1], req->data.args[2],
+      req->data.args[3], req->data.args[4], req->data.args[5]);
+
+  #if !defined(__MUSL__)
+  seccomp_print("FRAMES: (%u)\n", frames->len);
+  char **syms = backtrace_symbols(frames->items, frames->len);
+  if (syms == NULL) { FFATAL("Failed to get symbols"); }
+
+  for (guint i = 0; i < frames->len; i++) {
+
+    if (gum_symbol_details_from_address(frames->items[i], &details)) {
+
+      seccomp_print("\t%3d. %s!%s\n", i, details.module_name,
+                    details.symbol_name);
+
+    } else {
+
+      seccomp_print("\t%3d. %s\n", i, syms[i]);
+
+    }
+
+  }
+
+  free(syms);
+  #else
+  void **syms = (void **)__builtin_frame_address(0);
+  void * framep = __builtin_frame_address(1);
+  int    i = 0;
+
+  syms = framep;
+  while (syms) {
+
+    framep = *syms;
+    syms = framep;
+
+    if (!syms) break;
+
+    seccomp_print("\%3d. %s\n", i++, (char *)framep);
+
+  }
+
+  #endif
+
+  resp->error = 0;
+  resp->val = 0;
+  resp->id = req->id;
+  resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+
+}
+
+static void seccomp_callback_child(int signal_parent, void *ctx) {
+
+  int sock_fd = *((int *)ctx);
+  int fd = seccomp_socket_recv(sock_fd);
+
+  if (close(sock_fd) < 0) { FFATAL("child - close"); }
+
+  seccomp_event_signal(signal_parent);
+  seccomp_filter_child_install();
+  seccomp_filter_run(fd, seccomp_callback_filter);
+
+}
+
+void seccomp_callback_parent(void) {
+
+  int   sock[2] = {-1, -1};
+  pid_t child = -1;
+  int   child_fd = -1;
+
+  seccomp_socket_create(sock);
+  seccomp_child_run(seccomp_callback_child, sock, &child, &child_fd);
+
+  if (dup2(child_fd, SECCOMP_PARENT_EVENT_FD) < 0) { FFATAL("dup2"); }
+
+  if (close(child_fd) < 0) { FFATAL("seccomp_on_fork - close (1)"); }
+
+  if (close(sock[STDIN_FILENO]) < 0) { FFATAL("grandparent - close (2)"); }
+
+  int fd = seccomp_filter_install(child);
+  seccomp_socket_send(sock[STDOUT_FILENO], fd);
+
+  if (close(sock[STDOUT_FILENO]) < 0) { FFATAL("grandparent - close (3)"); }
+
+  if (close(fd) < 0) { FFATAL("grandparent - close (4)"); }
+
+  seccomp_child_wait(SECCOMP_PARENT_EVENT_FD);
+
+}
+
+void seccomp_callback_initialize(void) {
+
+  char *path = NULL;
+  int   fd;
+
+  path = g_canonicalize_filename(seccomp_filename, g_get_current_dir());
+
+  FOKF("Seccomp - path [%s]", path);
+
+  fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
+            S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+  if (dup2(fd, SECCOMP_OUTPUT_FILE_FD) < 0) {
+
+    FFATAL("Failed to duplicate seccomp output file");
+
+  }
+
+  if (close(fd) < 0) { FFATAL("Failed to close seccomp output file fd"); }
+
+  g_free(path);
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_child.c b/frida_mode/src/seccomp/seccomp_child.c
index 4d494137..c02ef67c 100644
--- a/frida_mode/src/seccomp/seccomp_child.c
+++ b/frida_mode/src/seccomp/seccomp_child.c
@@ -1,18 +1,19 @@
-#include <fcntl.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/types.h>
-#include <unistd.h>
+#if defined(__linux__) && !defined(__ANDROID__)
 
-#include "debug.h"
+  #include <fcntl.h>
+  #include <sched.h>
+  #include <signal.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #include <sys/mman.h>
+  #include <sys/prctl.h>
+  #include <sys/types.h>
+  #include <unistd.h>
 
-#include "seccomp.h"
+  #include "seccomp.h"
+  #include "util.h"
 
-#define SECCOMP_CHILD_STACK_SIZE (1UL << 20)
+  #define SECCOMP_CHILD_STACK_SIZE (1UL << 20)
 
 typedef void (*seccomp_child_func_t)(int event_fd, void *ctx);
 
@@ -49,11 +50,11 @@ void seccomp_child_run(seccomp_child_func_t child_func, void *ctx, pid_t *child,
   char *stack =
       (char *)mmap(NULL, SECCOMP_CHILD_STACK_SIZE, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (stack == MAP_FAILED) { FATAL("mmap"); }
+  if (stack == MAP_FAILED) { FFATAL("mmap"); }
 
   pid_t child_pid = clone(seccomp_child_func, &stack[SECCOMP_CHILD_STACK_SIZE],
                           flags, child_ctx, NULL, NULL, NULL);
-  if (child_pid < 0) { FATAL("clone"); }
+  if (child_pid < 0) { FFATAL("clone"); }
 
   if (child != NULL) { *child = child_pid; }
   if (event_fd != NULL) { *event_fd = fd; }
@@ -67,3 +68,5 @@ void seccomp_child_wait(int event_fd) {
 
 }
 
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_event.c b/frida_mode/src/seccomp/seccomp_event.c
index ecb9be32..e6585f1d 100644
--- a/frida_mode/src/seccomp/seccomp_event.c
+++ b/frida_mode/src/seccomp/seccomp_event.c
@@ -1,16 +1,23 @@
-#include <stdint.h>
-#include <stdio.h>
-#include <sys/eventfd.h>
-#include <unistd.h>
+#if defined(__linux__) && !defined(__ANDROID__)
 
-#include "debug.h"
+  #include <stdint.h>
+  #include <stdio.h>
+  #include <sys/syscall.h>
+  #include <unistd.h>
 
-#include "seccomp.h"
+  #include "seccomp.h"
+  #include "util.h"
 
 int seccomp_event_create(void) {
 
-  int fd = eventfd(0, 0);
-  if (fd < 0) { FATAL("seccomp_event_create"); }
+  #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;
 
 }
@@ -20,7 +27,7 @@ void seccomp_event_signal(int fd) {
   uint64_t val = 1;
   if (write(fd, &val, sizeof(uint64_t)) != sizeof(uint64_t)) {
 
-    FATAL("seccomp_event_signal");
+    FFATAL("seccomp_event_signal");
 
   }
 
@@ -31,7 +38,7 @@ void seccomp_event_wait(int fd) {
   uint64_t val = 1;
   if (read(fd, &val, sizeof(uint64_t)) != sizeof(uint64_t)) {
 
-    FATAL("seccomp_event_wait");
+    FFATAL("seccomp_event_wait");
 
   }
 
@@ -39,7 +46,9 @@ void seccomp_event_wait(int fd) {
 
 void seccomp_event_destroy(int fd) {
 
-  if (close(fd) < 0) { FATAL("seccomp_event_destroy"); }
+  if (close(fd) < 0) { FFATAL("seccomp_event_destroy"); }
 
 }
 
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_filter.c b/frida_mode/src/seccomp/seccomp_filter.c
index c16e7ebd..1d050303 100644
--- a/frida_mode/src/seccomp/seccomp_filter.c
+++ b/frida_mode/src/seccomp/seccomp_filter.c
@@ -1,27 +1,28 @@
-#include <alloca.h>
-#include <errno.h>
-#include <execinfo.h>
-#include <linux/filter.h>
-#include <linux/seccomp.h>
-#include <sys/ioctl.h>
-#include <sys/prctl.h>
-#include <sys/syscall.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "debug.h"
-
-#include "frida-gumjs.h"
-
-#include "seccomp.h"
-#include "util.h"
-
-#define SECCOMP_FILTER_NUM_FRAMES 512
+#if defined(__linux__) && !defined(__ANDROID__)
+
+  #include <alloca.h>
+  #include <errno.h>
+  #if !defined(__MUSL__)
+    #include <execinfo.h>
+  #endif
+  #include <linux/filter.h>
+  #include <sys/ioctl.h>
+  #include <sys/prctl.h>
+  #include <sys/syscall.h>
+  #include <signal.h>
+  #include <stdbool.h>
+  #include <stddef.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #include <string.h>
+  #include <unistd.h>
+
+  #include "frida-gumjs.h"
+
+  #include "seccomp.h"
+  #include "util.h"
+
+  #define SECCOMP_FILTER_NUM_FRAMES 512
 
 extern void gum_linux_parse_ucontext(const ucontext_t *uc, GumCpuContext *ctx);
 
@@ -71,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),
@@ -127,7 +134,10 @@ static GumBacktracer *       seccomp_filter_backtracer = NULL;
 static void seccomp_filter_child_handler(int sig, siginfo_t *info,
                                          void *ucontext) {
 
-  GumCpuContext cpu_context;
+  UNUSED_PARAMETER(sig);
+  UNUSED_PARAMETER(info);
+  UNUSED_PARAMETER(ucontext);
+
   if (seccomp_filter_backtracer == NULL) {
 
     seccomp_filter_backtracer = gum_backtracer_make_fuzzy();
@@ -150,9 +160,10 @@ static void seccomp_filter_parent_handler(int sig, siginfo_t *info,
   ucontext_t *uc = (ucontext_t *)ucontext;
   gum_linux_parse_ucontext(uc, &seccomp_filter_cpu_context);
 
-  if (tgkill(seccomp_filter_child, seccomp_filter_child, SIGUSR1) < 0) {
+  if (syscall(SYS_tgkill, seccomp_filter_child, seccomp_filter_child, SIGUSR1) <
+      0) {
 
-    FATAL("kill");
+    FFATAL("kill");
 
   }
 
@@ -165,7 +176,7 @@ void seccomp_filter_child_install(void) {
 
   const struct sigaction sa = {.sa_sigaction = seccomp_filter_child_handler,
                                .sa_flags = SA_SIGINFO | SA_RESTART};
-  if (sigaction(SIGUSR1, &sa, NULL) < 0) { FATAL("sigaction"); }
+  if (sigaction(SIGUSR1, &sa, NULL) < 0) { FFATAL("sigaction"); }
 
 }
 
@@ -180,17 +191,17 @@ int seccomp_filter_install(pid_t child) {
 
       .len = sizeof(filter) / sizeof(struct sock_filter), .filter = filter};
 
-  if (sigaction(SIGUSR1, &sa, NULL) < 0) { FATAL("sigaction"); }
+  if (sigaction(SIGUSR1, &sa, NULL) < 0) { FFATAL("sigaction"); }
 
   if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
 
-    FATAL("PR_SET_NO_NEW_PRIVS %d", errno);
+    FFATAL("PR_SET_NO_NEW_PRIVS %d", errno);
 
   }
 
   int fd = syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER,
                    SECCOMP_FILTER_FLAG_NEW_LISTENER, &filter_prog);
-  if (fd < 0) { FATAL("SYS_seccomp %d", fd); }
+  if (fd < 0) { FFATAL("SYS_seccomp %d", fd); }
 
   return fd;
 
@@ -204,19 +215,19 @@ void seccomp_filter_run(int fd, seccomp_filter_callback_t callback) {
 
   if (syscall(SYS_seccomp, SECCOMP_GET_NOTIF_SIZES, 0, &sizes) == -1) {
 
-    FATAL("seccomp-SECCOMP_GET_NOTIF_SIZES");
+    FFATAL("seccomp-SECCOMP_GET_NOTIF_SIZES");
 
   }
 
   if (sizes.seccomp_notif != sizeof(struct seccomp_notif)) {
 
-    FATAL("size - seccomp_notif");
+    FFATAL("size - seccomp_notif");
 
   }
 
   if (sizes.seccomp_notif_resp != sizeof(struct seccomp_notif_resp)) {
 
-    FATAL("size - seccomp_notif");
+    FFATAL("size - seccomp_notif");
 
   }
 
@@ -230,7 +241,7 @@ void seccomp_filter_run(int fd, seccomp_filter_callback_t callback) {
     if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req) < 0) {
 
       if (errno == EINTR) { continue; }
-      FATAL("SECCOMP_IOCTL_NOTIF_RECV: %d\n", fd);
+      FFATAL("SECCOMP_IOCTL_NOTIF_RECV: %d\n", fd);
 
     }
 
@@ -240,14 +251,14 @@ void seccomp_filter_run(int fd, seccomp_filter_callback_t callback) {
 
     } else {
 
-      if (kill(req->pid, SIGUSR1) < 0) { FATAL("kill"); }
+      if (kill(req->pid, SIGUSR1) < 0) { FFATAL("kill"); }
 
     }
 
     if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) {
 
       if (errno == ENOENT) { continue; }
-      OKF("SECCOMP_IOCTL_NOTIF_SEND");
+      FOKF("SECCOMP_IOCTL_NOTIF_SEND");
       continue;
 
     }
@@ -256,3 +267,5 @@ void seccomp_filter_run(int fd, seccomp_filter_callback_t callback) {
 
 }
 
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_print.c b/frida_mode/src/seccomp/seccomp_print.c
new file mode 100644
index 00000000..3cea1239
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_print.c
@@ -0,0 +1,30 @@
+#if defined(__linux__) && !defined(__ANDROID__)
+
+  #include <stdarg.h>
+
+  #include "seccomp.h"
+  #include "util.h"
+
+static void seccomp_print_v(int fd, char *format, va_list ap) {
+
+  char buffer[4096] = {0};
+  int  len;
+
+  if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; }
+
+  len = strnlen(buffer, sizeof(buffer));
+  IGNORED_RETURN(write(fd, buffer, len));
+
+}
+
+void seccomp_print(char *format, ...) {
+
+  va_list ap;
+  va_start(ap, format);
+  seccomp_print_v(SECCOMP_OUTPUT_FILE_FD, format, ap);
+  va_end(ap);
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_socket.c b/frida_mode/src/seccomp/seccomp_socket.c
index ca42e158..a01e88ee 100644
--- a/frida_mode/src/seccomp/seccomp_socket.c
+++ b/frida_mode/src/seccomp/seccomp_socket.c
@@ -1,11 +1,12 @@
-#include <stdio.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <unistd.h>
+#if defined(__linux__) && !defined(__ANDROID__)
 
-#include "debug.h"
+  #include <stdio.h>
+  #include <string.h>
+  #include <sys/socket.h>
+  #include <unistd.h>
 
-#include "seccomp.h"
+  #include "seccomp.h"
+  #include "util.h"
 
 union cmsg {
 
@@ -19,31 +20,31 @@ void seccomp_socket_create(int *sock) {
   int tmp_sock[2] = {-1, -1};
   if (socketpair(AF_UNIX, SOCK_STREAM, 0, tmp_sock) < 0) {
 
-    FATAL("socketpair");
+    FFATAL("socketpair");
 
   }
 
   if (dup2(tmp_sock[STDIN_FILENO], SECCOMP_SOCKET_RECV_FD) < 0) {
 
-    FATAL("seccomp_socket_create - dup2 (1)");
+    FFATAL("seccomp_socket_create - dup2 (1)");
 
   }
 
   if (dup2(tmp_sock[STDOUT_FILENO], SECCOMP_SOCKET_SEND_FD) < 0) {
 
-    FATAL("seccomp_socket_create - dup2 (1)");
+    FFATAL("seccomp_socket_create - dup2 (1)");
 
   }
 
   if (close(tmp_sock[STDIN_FILENO]) < 0) {
 
-    FATAL("seccomp_socket_create - close (1)");
+    FFATAL("seccomp_socket_create - close (1)");
 
   }
 
   if (close(tmp_sock[STDOUT_FILENO]) < 0) {
 
-    FATAL("seccomp_socket_create - close (2)");
+    FFATAL("seccomp_socket_create - close (2)");
 
   }
 
@@ -74,7 +75,7 @@ void seccomp_socket_send(int sockfd, int fd) {
 
   memcpy(CMSG_DATA(&control_msg.hdr), &fd, sizeof(int));
 
-  if (sendmsg(sockfd, &message, 0) == -1) { FATAL("sendmsg"); }
+  if (sendmsg(sockfd, &message, 0) == -1) { FFATAL("sendmsg"); }
 
 }
 
@@ -93,23 +94,23 @@ int seccomp_socket_recv(int sockfd) {
 
   int fd;
 
-  if (recvmsg(sockfd, &message, 0) < 0) { FATAL("recvmsg"); }
+  if (recvmsg(sockfd, &message, 0) < 0) { FFATAL("recvmsg"); }
 
   if (control_msg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) {
 
-    FATAL("control_msg.hdr.cmsg_len");
+    FFATAL("control_msg.hdr.cmsg_len");
 
   }
 
   if (control_msg.hdr.cmsg_level != SOL_SOCKET) {
 
-    FATAL("control_msg.hdr.cmsg_level");
+    FFATAL("control_msg.hdr.cmsg_level");
 
   }
 
   if (control_msg.hdr.cmsg_type != SCM_RIGHTS) {
 
-    FATAL("control_msg.hdr.cmsg_type");
+    FFATAL("control_msg.hdr.cmsg_type");
 
   }
 
@@ -119,3 +120,5 @@ int seccomp_socket_recv(int sockfd) {
 
 }
 
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_syscall.c b/frida_mode/src/seccomp/seccomp_syscall.c
index b2c084c8..2eac1af3 100644
--- a/frida_mode/src/seccomp/seccomp_syscall.c
+++ b/frida_mode/src/seccomp/seccomp_syscall.c
@@ -1,9 +1,10 @@
-#include <limits.h>
-#include <stdio.h>
+#if defined(__linux__) && !defined(__ANDROID__)
 
-#include "debug.h"
+  #include <limits.h>
+  #include <stdio.h>
 
-#include "seccomp.h"
+  #include "seccomp.h"
+  #include "util.h"
 
 typedef struct {
 
@@ -322,10 +323,10 @@ static syscall_entry_t seccomp_syscall_table[] = {
 
 char *seccomp_syscall_lookup(int id) {
 
-  if (id < 0) { FATAL("Invalid id: %d", id); }
+  if (id < 0) { FFATAL("Invalid id: %d", id); }
   if ((uint32_t)id >= sizeof(seccomp_syscall_table) / sizeof(syscall_entry_t)) {
 
-    FATAL("Invalid id: %d", id);
+    FFATAL("Invalid id: %d", id);
 
   }
 
@@ -333,3 +334,5 @@ char *seccomp_syscall_lookup(int id) {
 
 }
 
+#endif
+
diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c
index 814aaeb3..caa16b3f 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -1,4 +1,3 @@
-#include "debug.h"
 
 #include "instrument.h"
 #include "prefetch.h"
@@ -6,7 +5,9 @@
 #include "stats.h"
 #include "util.h"
 
-guint stalker_ic_entries = 0;
+guint    stalker_ic_entries = 0;
+gboolean backpatch_enable = TRUE;
+guint    stalker_adjacent_blocks = 0;
 
 static GumStalker *stalker = NULL;
 
@@ -56,9 +57,14 @@ static void gum_afl_stalker_observer_init(GumAflStalkerObserver *self) {
 
 void stalker_config(void) {
 
-  if (!gum_stalker_is_supported()) { FATAL("Failed to initialize embedded"); }
+  if (!gum_stalker_is_supported()) { FFATAL("Failed to initialize embedded"); }
 
-  stalker_ic_entries = util_read_num("AFL_FRIDA_STALKER_IC_ENTRIES");
+  backpatch_enable = (getenv("AFL_FRIDA_INST_NO_BACKPATCH") == NULL);
+
+  stalker_ic_entries = util_read_num("AFL_FRIDA_STALKER_IC_ENTRIES", 32);
+
+  stalker_adjacent_blocks =
+      util_read_num("AFL_FRIDA_STALKER_ADJACENT_BLOCKS", 32);
 
   observer = g_object_new(GUM_TYPE_AFL_STALKER_OBSERVER, NULL);
 
@@ -87,27 +93,50 @@ static gboolean stalker_exclude_self(const GumRangeDetails *details,
 
 void stalker_init(void) {
 
-  OKF("Stalker - ic_entries [%u]", stalker_ic_entries);
+  FOKF("Instrumentation - backpatch [%c]", backpatch_enable ? 'X' : ' ');
+
+  FOKF("Stalker - ic_entries [%u]", stalker_ic_entries);
+  FOKF("Stalker - adjacent_blocks [%u]", stalker_adjacent_blocks);
 
 #if !(defined(__x86_64__) || defined(__i386__))
-  if (stalker_ic_entries != 0) {
+  if (getenv("AFL_FRIDA_STALKER_IC_ENTRIES") != NULL) {
+
+    FFATAL("AFL_FRIDA_STALKER_IC_ENTRIES not supported");
+
+  }
+
+  if (getenv("AFL_FRIDA_STALKER_ADJACENT_BLOCKS") != NULL) {
 
-    FATAL("AFL_FRIDA_STALKER_IC_ENTRIES not supported");
+    FFATAL("AFL_FRIDA_STALKER_ADJACENT_BLOCKS not supported");
 
   }
 
 #endif
 
-  if (stalker_ic_entries == 0) { stalker_ic_entries = 32; }
+  if (instrument_coverage_filename != NULL) {
+
+    if (getenv("AFL_FRIDA_STALKER_ADJACENT_BLOCKS") != NULL) {
+
+      FFATAL(
+          "AFL_FRIDA_STALKER_ADJACENT_BLOCKS and AFL_FRIDA_INST_COVERAGE_FILE "
+          "are incompatible");
+
+    } else {
+
+      stalker_adjacent_blocks = 0;
+
+    }
+
+  }
 
 #if defined(__x86_64__) || defined(__i386__)
-  stalker =
-      g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries, NULL);
+  stalker = g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries,
+                         "adjacent-blocks", stalker_adjacent_blocks, NULL);
 #else
   stalker = gum_stalker_new();
 #endif
 
-  if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
+  if (stalker == NULL) { FFATAL("Failed to initialize stalker"); }
 
   gum_stalker_set_trust_threshold(stalker, -1);
 
@@ -118,7 +147,7 @@ void stalker_init(void) {
 
 GumStalker *stalker_get(void) {
 
-  if (stalker == NULL) { FATAL("Stalker uninitialized"); }
+  if (stalker == NULL) { FFATAL("Stalker uninitialized"); }
   return stalker;
 
 }
@@ -134,13 +163,13 @@ void stalker_start(void) {
 
 void stalker_trust(void) {
 
-  gum_stalker_set_trust_threshold(stalker, 0);
+  if (backpatch_enable) { gum_stalker_set_trust_threshold(stalker, 0); }
 
 }
 
 GumStalkerObserver *stalker_get_observer(void) {
 
-  if (observer == NULL) { FATAL("Stalker not yet initialized"); }
+  if (observer == NULL) { FFATAL("Stalker not yet initialized"); }
   return GUM_STALKER_OBSERVER(observer);
 
 }
diff --git a/frida_mode/src/stats/stats.c b/frida_mode/src/stats/stats.c
index 7972b881..83ecf89a 100644
--- a/frida_mode/src/stats/stats.c
+++ b/frida_mode/src/stats/stats.c
@@ -8,7 +8,6 @@
 #include "frida-gumjs.h"
 
 #include "config.h"
-#include "debug.h"
 #include "util.h"
 
 #include "entry.h"
@@ -324,42 +323,42 @@ static void stats_observer_init(GumStalkerObserver *observer) {
 void stats_config(void) {
 
   stats_filename = getenv("AFL_FRIDA_STATS_FILE");
-  stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL");
+  stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL", 10);
 
 }
 
 void stats_init(void) {
 
-  OKF("Stats - file [%s]", stats_filename);
-  OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval);
+  FOKF("Stats - file [%s]", stats_filename);
+  FOKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval);
 
-  if (stats_interval != 0 && stats_filename == NULL) {
+  if (getenv("AFL_FRIDA_STATS_INTERVAL") != NULL &&
+      getenv("AFL_FRIDA_STATS_FILE") == NULL) {
 
-    FATAL(
+    FFATAL(
         "AFL_FRIDA_STATS_FILE must be specified if "
         "AFL_FRIDA_STATS_INTERVAL is");
 
   }
 
-  if (stats_interval == 0) { stats_interval = 10; }
   stats_interval_us = stats_interval * MICRO_TO_SEC;
 
   if (stats_filename == NULL) { return; }
 
   char *path = g_canonicalize_filename(stats_filename, g_get_current_dir());
 
-  OKF("Stats - path [%s]", path);
+  FOKF("Stats - path [%s]", path);
 
   stats_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 
-  if (stats_fd < 0) { FATAL("Failed to open stats file '%s'", path); }
+  if (stats_fd < 0) { FFATAL("Failed to open stats file '%s'", path); }
 
   g_free(path);
 
   int shm_id =
       shmget(IPC_PRIVATE, sizeof(stats_data_t), IPC_CREAT | IPC_EXCL | 0600);
-  if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+  if (shm_id < 0) { FFATAL("shm_id < 0 - errno: %d\n", errno); }
 
   stats_data = shmat(shm_id, NULL, 0);
   g_assert(stats_data != MAP_FAILED);
@@ -372,7 +371,7 @@ void stats_init(void) {
    */
   if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
 
-    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+    FFATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
 
   }
 
diff --git a/frida_mode/src/stats/stats_arm32.c b/frida_mode/src/stats/stats_arm32.c
index 5860d33b..bd652aa3 100644
--- a/frida_mode/src/stats/stats_arm32.c
+++ b/frida_mode/src/stats/stats_arm32.c
@@ -1,7 +1,5 @@
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "stats.h"
 #include "util.h"
 
@@ -9,13 +7,13 @@
 
 void starts_arch_init(void) {
 
-  FATAL("Stats not supported on this architecture");
+  FFATAL("Stats not supported on this architecture");
 
 }
 
 void stats_write_arch(stats_data_t *data) {
 
-  FATAL("Stats not supported on this architecture");
+  FFATAL("Stats not supported on this architecture");
 
 }
 
@@ -23,7 +21,7 @@ void stats_collect_arch(const cs_insn *instr, gboolean begin) {
 
   UNUSED_PARAMETER(instr);
   UNUSED_PARAMETER(begin);
-  FATAL("Stats not supported on this architecture");
+  FFATAL("Stats not supported on this architecture");
 
 }
 
diff --git a/frida_mode/src/stats/stats_arm64.c b/frida_mode/src/stats/stats_arm64.c
index 54b3faf1..313ab47a 100644
--- a/frida_mode/src/stats/stats_arm64.c
+++ b/frida_mode/src/stats/stats_arm64.c
@@ -3,8 +3,6 @@
 
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "ranges.h"
 #include "stats.h"
 #include "util.h"
@@ -48,7 +46,7 @@ void starts_arch_init(void) {
 
   int shm_id = shmget(IPC_PRIVATE, sizeof(stats_data_arch_t),
                       IPC_CREAT | IPC_EXCL | 0600);
-  if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+  if (shm_id < 0) { FFATAL("shm_id < 0 - errno: %d\n", errno); }
 
   stats_data_arch = shmat(shm_id, NULL, 0);
   g_assert(stats_data_arch != MAP_FAILED);
@@ -58,7 +56,7 @@ void starts_arch_init(void) {
    */
   if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
 
-    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+    FFATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
 
   }
 
diff --git a/frida_mode/src/stats/stats_x86_64.c b/frida_mode/src/stats/stats_x86_64.c
index ab914951..0bfe3baa 100644
--- a/frida_mode/src/stats/stats_x86_64.c
+++ b/frida_mode/src/stats/stats_x86_64.c
@@ -3,8 +3,6 @@
 
 #include "frida-gumjs.h"
 
-#include "debug.h"
-
 #include "ranges.h"
 #include "stats.h"
 #include "util.h"
@@ -50,7 +48,7 @@ void starts_arch_init(void) {
 
   int shm_id = shmget(IPC_PRIVATE, sizeof(stats_data_arch_t),
                       IPC_CREAT | IPC_EXCL | 0600);
-  if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+  if (shm_id < 0) { FFATAL("shm_id < 0 - errno: %d\n", errno); }
 
   stats_data_arch = shmat(shm_id, NULL, 0);
   g_assert(stats_data_arch != MAP_FAILED);
@@ -60,7 +58,7 @@ void starts_arch_init(void) {
    */
   if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
 
-    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+    FFATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
 
   }
 
@@ -255,8 +253,8 @@ static x86_op_type stats_get_operand_type(const cs_insn *instr) {
 
   if (x86->op_count != 1) {
 
-    FATAL("Unexpected operand count (%d): %s %s\n", x86->op_count,
-          instr->mnemonic, instr->op_str);
+    FFATAL("Unexpected operand count (%d): %s %s\n", x86->op_count,
+           instr->mnemonic, instr->op_str);
 
   }
 
@@ -295,7 +293,7 @@ static void stats_collect_call_arch(const cs_insn *instr) {
       stats_data_arch->num_call_mem++;
       break;
     default:
-      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
+      FFATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
 
   }
 
@@ -316,7 +314,7 @@ static void stats_collect_jump_arch(const cs_insn *instr) {
       stats_data_arch->num_jmp_mem++;
       break;
     default:
-      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
+      FFATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
 
   }
 
@@ -337,7 +335,7 @@ static void stats_collect_jump_cond_arch(const cs_insn *instr) {
       stats_data_arch->num_jmp_cond_mem++;
       break;
     default:
-      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
+      FFATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
 
   }
 
diff --git a/frida_mode/src/util.c b/frida_mode/src/util.c
index 09e8a58b..6f52b6cb 100644
--- a/frida_mode/src/util.c
+++ b/frida_mode/src/util.c
@@ -1,12 +1,11 @@
 #include "util.h"
 
-#include "debug.h"
-
-guint64 util_read_address(char *key) {
+guint64 util_read_address(char *key, guint64 default_value) {
 
   char *value_str = getenv(key);
+  char *end_ptr;
 
-  if (value_str == NULL) { return 0; }
+  if (value_str == NULL) { return default_value; }
 
   if (!g_str_has_prefix(value_str, "0x")) {
 
@@ -27,8 +26,17 @@ guint64 util_read_address(char *key) {
 
   }
 
-  guint64 value = g_ascii_strtoull(value_str2, NULL, 16);
-  if (value == 0) {
+  errno = 0;
+
+  guint64 value = g_ascii_strtoull(value_str2, &end_ptr, 16);
+
+  if (errno != 0) {
+
+    FATAL("Error (%d) during conversion: %s", errno, value_str);
+
+  }
+
+  if (value == 0 && end_ptr == value_str2) {
 
     FATAL("Invalid address failed hex conversion: %s=%s\n", key, value_str2);
 
@@ -38,11 +46,12 @@ guint64 util_read_address(char *key) {
 
 }
 
-guint64 util_read_num(char *key) {
+guint64 util_read_num(char *key, guint64 default_value) {
 
   char *value_str = getenv(key);
+  char *end_ptr;
 
-  if (value_str == NULL) { return 0; }
+  if (value_str == NULL) { return default_value; }
 
   for (char *c = value_str; *c != '\0'; c++) {
 
@@ -55,8 +64,17 @@ guint64 util_read_num(char *key) {
 
   }
 
+  errno = 0;
+
   guint64 value = g_ascii_strtoull(value_str, NULL, 10);
-  if (value == 0) {
+
+  if (errno != 0) {
+
+    FATAL("Error (%d) during conversion: %s", errno, value_str);
+
+  }
+
+  if (value == 0 && end_ptr == value_str) {
 
     FATAL("Invalid address failed numeric conversion: %s=%s\n", key, value_str);
 
@@ -66,3 +84,40 @@ guint64 util_read_num(char *key) {
 
 }
 
+gboolean util_output_enabled(void) {
+
+  static gboolean initialized = FALSE;
+  static gboolean enabled = TRUE;
+
+  if (!initialized) {
+
+    initialized = TRUE;
+    if (getenv("AFL_DEBUG_CHILD") == NULL) { enabled = FALSE; }
+
+  }
+
+  return enabled;
+
+}
+
+gsize util_rotate(gsize val, gsize shift, gsize size) {
+
+  if (shift == 0) { return val; }
+  gsize result = ((val >> shift) | (val << (size - shift)));
+  result = result & ((1 << size) - 1);
+  return result;
+
+}
+
+gsize util_log2(gsize val) {
+
+  for (gsize i = 0; i < 64; i++) {
+
+    if (((gsize)1 << i) == val) { return i; }
+
+  }
+
+  FFATAL("Not a power of two");
+
+}
+
diff --git a/frida_mode/test/bloaty/GNUmakefile b/frida_mode/test/bloaty/GNUmakefile
new file mode 100644
index 00000000..7ef4fe81
--- /dev/null
+++ b/frida_mode/test/bloaty/GNUmakefile
@@ -0,0 +1,114 @@
+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
+
+# LIBFUZZER_LIB:=/usr/lib/llvm-12/lib/libFuzzer.a
+
+BLOATY_GIT_REPO:=https://github.com/google/bloaty.git
+BLOATY_DIR:=$(BUILD_DIR)bloaty/
+TEST_BIN:=$(BLOATY_DIR)fuzz_target
+
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie
+endif
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+TEST_DATA_SRC:=$(BLOATY_DIR)tests/testdata/fuzz_corpus/
+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 $@
+
+########## BLOATY #######
+
+$(BLOATY_DIR): | $(BUILD_DIR)
+	git clone --depth 1 $(BLOATY_GIT_REPO) $@
+
+$(TEST_BIN): $(BLOATY_DIR)
+	cd $(BLOATY_DIR) && CC=clang CXX=clang++ CCC=clang++ LIB_FUZZING_ENGINE="-fsanitize=fuzzer" cmake -G Ninja -DBUILD_TESTING=false $(BLOATY_DIR)
+	cd $(BLOATY_DIR) && CC=clang CXX=clang++ CCC=clang++ ninja -j $(shell nproc)
+
+########## DUMMY #######
+
+$(TEST_DATA_DIR): | $(BLOATY_DIR) $(BUILD_DIR)
+	cp -av $(TEST_DATA_SRC) $@
+
+$(DUMMY_DATA_FILE): | $(TEST_DATA_DIR)
+	dd if=/dev/zero bs=1048576 count=1 of=$@
+
+###### TEST DATA #######
+
+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/bloaty/Makefile b/frida_mode/test/bloaty/Makefile
new file mode 100644
index 00000000..07b139e9
--- /dev/null
+++ b/frida_mode/test/bloaty/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/bloaty/get_symbol_addr.py b/frida_mode/test/bloaty/get_symbol_addr.py
new file mode 100755
index 00000000..1c46e010
--- /dev/null
+++ b/frida_mode/test/bloaty/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/test/freetype2/GNUmakefile b/frida_mode/test/freetype2/GNUmakefile
new file mode 100644
index 00000000..f7a50de2
--- /dev/null
+++ b/frida_mode/test/freetype2/GNUmakefile
@@ -0,0 +1,192 @@
+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
+
+# git clone git://git.sv.nongnu.org/freetype/freetype2.git
+# git clone https://github.com/unicode-org/text-rendering-tests.git TRT
+# wget https://github.com/libarchive/libarchive/releases/download/v3.4.3/libarchive-3.4.3.tar.xz
+
+# cp TRT/fonts/TestKERNOne.otf $OUT/seeds/
+# cp TRT/fonts/TestGLYFOne.ttf $OUT/seeds/
+
+# $CXX $CXXFLAGS -std=c++11 -I include -I . src/tools/ftfuzzer/ftfuzzer.cc \
+#     objs/.libs/libfreetype.a $FUZZER_LIB -L /usr/local/lib -larchive \
+#     -o $OUT/ftfuzzer
+
+LIBARCHIVE_URL:=https://github.com/libarchive/libarchive/releases/download/v3.4.3/libarchive-3.4.3.tar.xz
+LIBARCHIVE_BUILD_DIR:=$(BUILD_DIR)libarchive/
+LIBARCHIVE_TARBALL:=$(LIBARCHIVE_BUILD_DIR)libarchive-3.4.3.tar.xz
+LIBARCHIVE_DIR:=$(LIBARCHIVE_BUILD_DIR)libarchive-3.4.3/
+LIBARCHIVE_LIB:=$(LIBARCHIVE_DIR).libs/libarchive.a
+
+FREETYPE2_GIT_REPO:=git://git.sv.nongnu.org/freetype/freetype2.git
+FREETYPE2_BUILD_DIR:=$(BUILD_DIR)freetype2/
+FREETYPE2_DIR:=$(FREETYPE2_BUILD_DIR)freetype2/
+FREETYPE2_LIB:=$(FREETYPE2_DIR)objs/.libs/libfreetype.a
+
+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
+
+TRT_GIT_REPO:=https://github.com/unicode-org/text-rendering-tests.git
+TRT_DIR:=$(BUILD_DIR)TRT/
+
+FUZZER_SRC:=$(FREETYPE2_DIR)src/tools/ftfuzzer/ftfuzzer.cc
+
+
+LDFLAGS += -lpthread
+
+TEST_BIN:=$(BUILD_DIR)test
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie
+endif
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+TEST_DATA_FILE:=$(TEST_DATA_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:
+	CFLAGS="-m32" CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+########## LIBARCHIVE #######
+
+$(LIBARCHIVE_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(LIBARCHIVE_TARBALL): | $(LIBARCHIVE_BUILD_DIR)
+	wget -O $@ $(LIBARCHIVE_URL)
+
+$(LIBARCHIVE_DIR): | $(LIBARCHIVE_TARBALL)
+	tar Jxvf $(LIBARCHIVE_TARBALL) -C $(LIBARCHIVE_BUILD_DIR)
+
+$(LIBARCHIVE_DIR)Makefile: | $(LIBARCHIVE_DIR)
+	cd $(LIBARCHIVE_DIR) && ./configure --disable-shared
+
+$(LIBARCHIVE_LIB): $(LIBARCHIVE_DIR)Makefile
+	make -C $(LIBARCHIVE_DIR) clean all
+
+########## FREETYPE2 #######
+
+$(FREETYPE2_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(FREETYPE2_DIR): | $(FREETYPE2_BUILD_DIR)
+	git clone $(FREETYPE2_GIT_REPO) $@
+	git -C $(FREETYPE2_DIR) checkout cd02d359a6d0455e9d16b87bf9665961c4699538
+
+$(FREETYPE2_LIB): | $(FREETYPE2_DIR)
+	cd $(FREETYPE2_DIR) && ./autogen.sh
+	cd $(FREETYPE2_DIR) && ./configure --with-harfbuzz=no --with-bzip2=no --with-png=no --without-zlib
+	make -C $(FREETYPE2_DIR) all
+
+########## HARNESS #######
+
+$(HARNESS_SRC):
+	wget -O $@ $(HARNESS_URL)
+
+$(HARNESS_OBJ): $(HARNESS_SRC)
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+########## TEST #######
+
+$(TEST_BIN): $(LIBARCHIVE_LIB) $(FREETYPE2_LIB) $(HARNESS_OBJ)
+	$(CXX) \
+		$(CXXFLAGS) \
+		-std=c++11 \
+		-I $(FREETYPE2_DIR)include \
+		-I $(FREETYPE2_DIR) \
+		-I $(LIBARCHIVE_DIR)/libarchive \
+		$(FUZZER_SRC) \
+     	$(FREETYPE2_LIB) \
+		$(LIBARCHIVE_LIB) \
+		$(HARNESS_OBJ) \
+	    -o $@
+
+########## DUMMY #######
+
+$(TRT_DIR): | $(BUILD_DIR)
+	git clone $(TRT_GIT_REPO) $@
+
+$(TEST_DATA_DIR): | $(TRT_DIR)
+	mkdir -p $@
+	cp $(TRT_DIR)fonts/TestKERNOne.otf $@
+	cp $(TRT_DIR)fonts/TestGLYFOne.ttf $@
+
+$(TEST_DATA_FILE): | $(TEST_DATA_DIR)
+	dd if=/dev/zero bs=1048576 count=1 of=$@
+
+###### TEST DATA #######
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(TEST_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) $(TEST_DATA_FILE)
+
+qemu: $(TEST_BIN) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) $(TEST_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) $(TEST_DATA_FILE)
diff --git a/frida_mode/test/freetype2/Makefile b/frida_mode/test/freetype2/Makefile
new file mode 100644
index 00000000..07b139e9
--- /dev/null
+++ b/frida_mode/test/freetype2/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/freetype2/get_symbol_addr.py b/frida_mode/test/freetype2/get_symbol_addr.py
new file mode 100755
index 00000000..1c46e010
--- /dev/null
+++ b/frida_mode/test/freetype2/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/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/libpcap/GNUmakefile b/frida_mode/test/libpcap/GNUmakefile
index 6f2b58af..f8dc3db7 100644
--- a/frida_mode/test/libpcap/GNUmakefile
+++ b/frida_mode/test/libpcap/GNUmakefile
@@ -59,7 +59,7 @@ 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"
+ifeq "$(ARCH)" "arm64"
  AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
 endif
 
diff --git a/frida_mode/test/perf/GNUmakefile b/frida_mode/test/perf/GNUmakefile
new file mode 100644
index 00000000..d65aaa6d
--- /dev/null
+++ b/frida_mode/test/perf/GNUmakefile
@@ -0,0 +1,116 @@
+PWD:=$(shell pwd)/
+ROOT:=$(PWD)../../../
+BUILD_DIR:=$(PWD)build/
+
+TEST_BIN:=$(BUILD_DIR)perf
+TEST_SRC:=$(PWD)perf.c
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+TEST_DATA_FILE:=$(TEST_DATA_DIR)default_seed
+
+QEMU_OUT:=$(BUILD_DIR)qemu-out
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
+AFLPP_QEMU_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/qemu_hook.so
+
+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)" "arm64"
+ 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 32 clean qemu frida
+
+all: $(TEST_BIN)
+	make -C $(ROOT)frida_mode/
+
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+$(PERF_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(PERF_DATA_FILE): | $(PERF_DATA_DIR)
+	echo -n "000" > $@
+
+$(TEST_BIN): $(TEST_SRC) | $(BUILD_DIR)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+$(TEST_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(TEST_DATA_FILE): | $(TEST_DATA_DIR)
+	dd if=/dev/zero bs=1048576 count=1 of=$@
+
+
+qemu: $(TEST_BIN) $(TEST_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) \
+	AFL_QEMU_PERSISTENT_GPR=1 \
+	$(ROOT)afl-fuzz \
+		-D \
+		-Q \
+		-i $(TEST_DATA_DIR) \
+		-o $(QEMU_OUT) \
+		-V 10 \
+		-- \
+			$(TEST_BIN) $(TEST_DATA_FILE)
+
+frida: $(TEST_BIN) $(TEST_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 \
+		-D \
+		-O \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-V 10 \
+		-- \
+			$(TEST_BIN) $(TEST_DATA_FILE)
+
+debug:
+	echo $(AFL_FRIDA_PERSISTENT_ADDR)
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TEST_BIN) $(TEST_DATA_FILE)
+
+debug:
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TEST_BIN) $(TEST_DATA_FILE)
diff --git a/frida_mode/test/perf/Makefile b/frida_mode/test/perf/Makefile
new file mode 100644
index 00000000..f843af19
--- /dev/null
+++ b/frida_mode/test/perf/Makefile
@@ -0,0 +1,19 @@
+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
+
+qemu:
+	@gmake qemu
+
+frida:
+	@gmake frida
+
+debug:
+	@gmake debug
diff --git a/frida_mode/test/perf/perf.c b/frida_mode/test/perf/perf.c
new file mode 100644
index 00000000..c5881915
--- /dev/null
+++ b/frida_mode/test/perf/perf.c
@@ -0,0 +1,105 @@
+/*
+   american fuzzy lop++ - a trivial program to test the build
+   --------------------------------------------------------
+   Originally written by Michal Zalewski
+   Copyright 2014 Google Inc. All rights reserved.
+   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:
+     http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void LLVMFuzzerTestOneInput(char *buf, int len) {
+
+  int ret = 0;
+  for (int i = 0; i < 1000; i++) {
+    switch(buf[i]) {
+      case 'A': ret += 2; break;
+      case '1': ret += 3; break;
+      default: ret++;
+    }
+  }
+  printf("ret: %d\n", ret);
+
+}
+
+int main(int argc, char **argv) {
+
+  char * file;
+  int    fd = -1;
+  off_t  len;
+  char * buf = NULL;
+  size_t n_read;
+  int    result = -1;
+
+  if (argc != 2) { return 1; }
+
+  do {
+
+    file = argv[1];
+
+    dprintf(STDERR_FILENO, "Running: %s\n", file);
+
+    fd = open(file, O_RDONLY);
+    if (fd < 0) {
+
+      perror("open");
+      break;
+
+    }
+
+    len = lseek(fd, 0, SEEK_END);
+    if (len < 0) {
+
+      perror("lseek (SEEK_END)");
+      break;
+
+    }
+
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+
+      perror("lseek (SEEK_SET)");
+      break;
+
+    }
+
+    buf = malloc(len);
+    if (buf == NULL) {
+
+      perror("malloc");
+      break;
+
+    }
+
+    n_read = read(fd, buf, len);
+    if (n_read != len) {
+
+      perror("read");
+      break;
+
+    }
+
+    dprintf(STDERR_FILENO, "Running:    %s: (%zd bytes)\n", file, n_read);
+
+    LLVMFuzzerTestOneInput(buf, len);
+    dprintf(STDERR_FILENO, "Done:    %s: (%zd bytes)\n", file, n_read);
+
+    result = 0;
+
+  } while (false);
+
+  if (buf != NULL) { free(buf); }
+
+  if (fd != -1) { close(fd); }
+
+  return result;
+
+}
+
diff --git a/frida_mode/test/unstable/GNUmakefile b/frida_mode/test/unstable/GNUmakefile
index 0ccc5fb1..54bbe662 100644
--- a/frida_mode/test/unstable/GNUmakefile
+++ b/frida_mode/test/unstable/GNUmakefile
@@ -86,11 +86,23 @@ frida: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
 			$(UNSTABLE_BIN) @@
 
 frida_coverage: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
-	AFL_DEBUG=1 \
 	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
 	AFL_FRIDA_OUTPUT_STDOUT=/tmp/stdout.txt \
     AFL_FRIDA_OUTPUT_STDERR=/tmp/stderr.txt \
 	AFL_FRIDA_INST_COVERAGE_FILE=/tmp/coverage.dat \
+	$(ROOT)afl-fuzz \
+		-D \
+		-O \
+		-i $(UNSTABLE_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-- \
+			$(UNSTABLE_BIN) @@
+
+frida_unstable: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
+	AFL_DEBUG=1 \
+	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	AFL_FRIDA_OUTPUT_STDOUT=/tmp/stdout.txt \
+    AFL_FRIDA_OUTPUT_STDERR=/tmp/stderr.txt \
     AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE=/tmp/unstable.dat \
 	$(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 8a1ebf1b..0473cbf6 100644
--- a/frida_mode/ts/lib/afl.ts
+++ b/frida_mode/ts/lib/afl.ts
@@ -78,6 +78,13 @@ class Afl {
   }
 
   /**
+   * See `AFL_FRIDA_INST_NO_BACKPATCH`.
+   */
+  public static setBackpatchDisable(): void {
+    Afl.jsApiSetBackpatchDisable();
+  }
+
+  /**
    * See `AFL_FRIDA_DEBUG_MAPS`.
    */
   public static setDebugMaps(): void {
@@ -172,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.
@@ -234,6 +249,13 @@ class Afl {
     Afl.jsApiSetSeccompFile(buf);
   }
 
+  /**
+   * See `AFL_FRIDA_STALKER_ADJACENT_BLOCKS`.
+   */
+  public static setStalkerAdjacentBlocks(val: number): void {
+    Afl.jsApiSetStalkerAdjacentBlocks(val);
+  }
+
   /*
    * Set a function to be called for each instruction which is instrumented
    * by AFL FRIDA mode.
@@ -284,6 +306,13 @@ class Afl {
     Afl.jsApiSetStdOut(buf);
   }
 
+  /**
+   * See `AFL_FRIDA_TRACEABLE`.
+   */
+  public static setTraceable(): void {
+    Afl.jsApiSetTraceable();
+  }
+
   private static readonly jsApiAddExcludeRange = Afl.jsApiGetFunction(
     "js_api_add_exclude_range",
     "void",
@@ -306,6 +335,11 @@ class Afl {
     "void",
     ["pointer"]);
 
+  private static readonly jsApiSetBackpatchDisable = Afl.jsApiGetFunction(
+    "js_api_set_backpatch_disable",
+    "void",
+    []);
+
   private static readonly jsApiSetDebugMaps = Afl.jsApiGetFunction(
     "js_api_set_debug_maps",
     "void",
@@ -361,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",
@@ -401,6 +440,11 @@ class Afl {
     "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",
@@ -431,6 +475,11 @@ class Afl {
     "void",
     ["pointer"]);
 
+  private static readonly jsApiSetTraceable = Afl.jsApiGetFunction(
+    "js_api_set_traceable",
+    "void",
+    []);
+
   private static readonly jsApiWrite = new NativeFunction(
     /* tslint:disable-next-line:no-null-keyword */
     Module.getExportByName(null, "write"),
diff --git a/frida_mode/ub1804/Dockerfile b/frida_mode/ub1804/Dockerfile
new file mode 100644
index 00000000..e1f5a339
--- /dev/null
+++ b/frida_mode/ub1804/Dockerfile
@@ -0,0 +1,6 @@
+FROM ubuntu:xenial
+
+WORKDIR /AFLplusplus
+
+RUN apt-get update
+RUN apt-get install -y make gcc g++ xz-utils curl wget clang zlib1g-dev
\ No newline at end of file
diff --git a/frida_mode/ub1804/GNUmakefile b/frida_mode/ub1804/GNUmakefile
new file mode 100644
index 00000000..4247916e
--- /dev/null
+++ b/frida_mode/ub1804/GNUmakefile
@@ -0,0 +1,37 @@
+PWD:=$(shell pwd)/
+ROOT:=$(PWD)../../
+
+.PHONY: all build docker clean shell test
+
+all: docker
+	docker run --rm \
+		-v $(ROOT):/AFLplusplus \
+		ub1804-afl-frida \
+		/bin/sh -c \
+			'make -C /AFLplusplus/ clean all && \
+			make -C /AFLplusplus/frida_mode clean all'
+
+test:
+	docker run --rm \
+		-v $(ROOT):/AFLplusplus \
+		ub1804-afl-frida \
+		/bin/sh -c \
+			'make -C /AFLplusplus/frida_mode/test/png clean all && \
+			make -C /AFLplusplus/frida_mode/test/png/persistent/hook frida'
+
+build:
+	docker run --rm \
+		-v $(ROOT):/AFLplusplus \
+		ub1804-afl-frida \
+		/bin/sh -c \
+			'make -C /AFLplusplus/ all && \
+			make -C /AFLplusplus/frida_mode all'
+
+docker:
+	docker build --tag ub1804-afl-frida .
+
+clean:
+	docker images --filter 'dangling=true' -q --no-trunc | xargs -L1 docker rmi --force
+
+shell:
+	docker run -ti --rm ub1804-afl-frida /bin/bash
diff --git a/frida_mode/ub1804/Makefile b/frida_mode/ub1804/Makefile
new file mode 100644
index 00000000..f3c3cd55
--- /dev/null
+++ b/frida_mode/ub1804/Makefile
@@ -0,0 +1,9 @@
+all:
+	@echo trying to use GNU make...
+	@gmake all || echo please install GNUmake
+
+clean:
+	@gmake clean
+
+shell:
+	@gmake shell