about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2023-06-05 14:12:56 +0300
committerGitHub <noreply@github.com>2023-06-05 14:12:56 +0300
commitf9b72b6f2f9a6545143ad2efa40ea0b527a5bf5c (patch)
tree82620ccfd9079b2e4fd0f7a6176b20508d4b68ca
parented73c632a5791ca740fe64770b6d238206033ec4 (diff)
parentb644e48f36485c645cbc0dadf0fddb2aa14cc079 (diff)
downloadafl++-f9b72b6f2f9a6545143ad2efa40ea0b527a5bf5c.tar.gz
Merge pull request #1755 from AFLplusplus/dev
push to stable
-rw-r--r--GNUmakefile.llvm4
-rw-r--r--TODO.md2
-rwxr-xr-xafl-cmin.bash4
-rw-r--r--docs/Changelog.md5
-rw-r--r--frida_mode/include/entry.h2
-rw-r--r--frida_mode/src/entry.c26
-rw-r--r--frida_mode/src/instrument/instrument.c1
-rw-r--r--frida_mode/src/instrument/instrument_arm64.c231
-rw-r--r--frida_mode/src/main.c41
-rw-r--r--frida_mode/test/testinstr/GNUmakefile5
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc2
-rw-r--r--instrumentation/SanitizerCoveragePCGUARD.so.cc601
-rw-r--r--src/afl-cc.c31
13 files changed, 483 insertions, 472 deletions
diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm
index 2bb4e7f8..6c68f1f3 100644
--- a/GNUmakefile.llvm
+++ b/GNUmakefile.llvm
@@ -49,7 +49,7 @@ LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^[
 LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[5-9]' && echo 1 || echo 0 )
 LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[0-9]' && echo 1 || echo 0 )
 LLVM_NEWER_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[6-9]' && echo 1 || echo 0 )
-LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 )
+LLVM_13_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[3-9]' && echo 1 || echo 0 )
 LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]' && echo 1 || echo 0 )
 LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)
 LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
@@ -422,7 +422,7 @@ endif
 	$(CXX) $(CLANG_CPPFL) -Wdeprecated -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
 
 ./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps
-ifeq "$(LLVM_10_OK)" "1"
+ifeq "$(LLVM_13_OK)" "1"
 	-$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) -Wno-deprecated-copy-dtor -Wdeprecated instrumentation/afl-llvm-common.o
 endif
 
diff --git a/TODO.md b/TODO.md
index dc02a914..2b7e8fcf 100644
--- a/TODO.md
+++ b/TODO.md
@@ -2,9 +2,7 @@
 
 ## Should
 
- - redo PCGUARD + LTO for llvm 15+
  - test cmplog for less than 16bit
- - splicing selection weighted?
  - support persistent and deferred fork server in afl-showmap?
  - better autodetection of shifting runtime timeout values
  - Update afl->pending_not_fuzzed for MOpt
diff --git a/afl-cmin.bash b/afl-cmin.bash
index d390ff65..dc6d5342 100755
--- a/afl-cmin.bash
+++ b/afl-cmin.bash
@@ -206,7 +206,7 @@ fi
 
 # Check for obvious errors.
 
-if [ ! "$T_ARG" = "" -a ! "$F_ARG" = "" -a ! "$NYX_MODE" == 1 ]; then
+if [ ! "$T_ARG" = "" -a -n "$F_ARG" -a ! "$NYX_MODE" == 1 ]; then
   echo "[-] Error: -T and -f can not be used together." 1>&2
   exit 1
 fi
@@ -323,7 +323,7 @@ if [ ! "$T_ARG" = "" ]; then
     fi
   fi
 else
-  if [ "$F_ARG" = ""]; then
+  if [ -z "$F_ARG" ]; then
     echo "[*] Are you aware of the '-T all' parallelize option that massively improves the speed?"
   fi
 fi
diff --git a/docs/Changelog.md b/docs/Changelog.md
index e99747f6..facf2196 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -15,13 +15,16 @@
     - new env `AFL_LLVM_LTO_SKIPINIT` to support the AFL++ based WASM
       (https://github.com/fgsect/WAFL) project
     - error and print help if afl-clan-lto is used with lto=thin
+    - rewrote our PCGUARD pass to be compatible with LLVM 15+ shenanigans,
+      requires LLVM 13+ now instead of 10.0.1+
+    - fallback to native LLVM PCGUARD if our PCGUARD is unavailable
   - afl-showmap:
     - added custom mutator post_process and send support
     - add `-I filelist` option, an alternative to `-i in_dir`
   - afl-cmin + afl-cmin.bash:
     - `-T threads` parallel task support, can be a huge speedup!
   - qemu_mode:
-    - Persistent mode +QASAN support for ppc32 tragets by @worksbutnottested
+    - Persistent mode + QASAN support for ppc32 targets by @worksbutnottested
   - a new grammar custom mutator atnwalk was submitted by @voidptr127 !
   - two new custom mutators are now available:
     - TritonDSE in custom_mutators/aflpp_tritondse
diff --git a/frida_mode/include/entry.h b/frida_mode/include/entry.h
index edc41467..949fab71 100644
--- a/frida_mode/include/entry.h
+++ b/frida_mode/include/entry.h
@@ -14,8 +14,6 @@ void entry_init(void);
 
 void entry_start(void);
 
-void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
-
 void entry_on_fork(void);
 
 #endif
diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c
index 05af7ebb..fc49b4d7 100644
--- a/frida_mode/src/entry.c
+++ b/frida_mode/src/entry.c
@@ -78,31 +78,7 @@ void entry_init(void) {
 
 void entry_start(void) {
 
-  if (persistent_start == 0) {
-
-    ranges_exclude();
-    stalker_trust();
-
-  }
-
-  if (entry_point == 0) { entry_launch(); }
-
-}
-
-static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
-
-  UNUSED_PARAMETER(cpu_context);
-  UNUSED_PARAMETER(user_data);
-  entry_compiled = TRUE;
-  entry_launch();
-
-}
-
-void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
-
-  UNUSED_PARAMETER(output);
   FVERBOSE("AFL_ENTRYPOINT reached");
-
   if (persistent_start == 0) {
 
     ranges_exclude();
@@ -110,7 +86,7 @@ void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
 
   }
 
-  gum_stalker_iterator_put_callout(iterator, entry_callout, NULL, NULL);
+  entry_launch();
 
 }
 
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index a6aac666..db73d845 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -169,7 +169,6 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
 
     if (unlikely(begin)) { instrument_debug_start(instr->address, output); }
 
-    if (instr->address == entry_point) { entry_prologue(iterator, output); }
     if (instr->address == persistent_start) { persistent_prologue(output); }
     if (instr->address == persistent_ret) { persistent_epilogue(output); }
 
diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c
index 4372861d..131eb4c5 100644
--- a/frida_mode/src/instrument/instrument_arm64.c
+++ b/frida_mode/src/instrument/instrument_arm64.c
@@ -76,6 +76,45 @@ typedef struct {
 
 } afl_log_code_asm_t;
 
+typedef struct {
+
+  uint32_t b_imm8;                                          /* br #XX (end) */
+
+  uint32_t restoration_prolog;                 /* ldp x16, x17, [sp], #0x90 */
+
+  uint32_t stp_x0_x1;                           /* stp x0, x1, [sp, #-0xa0] */
+
+  uint32_t ldr_x0_p_prev_loc_1;                          /* ldr x0, #0xXXXX */
+  uint32_t ldr_x1_ptr_x0;                                   /* ldr x1, [x0] */
+
+  uint32_t ldr_x0_p_area_offset;                         /* ldr x0, #0xXXXX */
+  uint32_t eor_x0_x1_x0;                                  /* eor x0, x1, x0 */
+  uint32_t ldr_x1_p_area_ptr;                            /* ldr x1, #0xXXXX */
+  uint32_t add_x0_x1_x0;                                  /* add x0, x1, x0 */
+
+  uint32_t ldrb_w1_x0;                                     /* ldrb w1, [x0] */
+  uint32_t add_w1_w1_1;                                   /* add w1, w1, #1 */
+  uint32_t add_w1_w1_w1_lsr_8;                    /* add x1, x1, x1, lsr #8 */
+
+  uint32_t strb_w1_ptr_x0;                                 /* strb w1, [x0] */
+
+  uint32_t ldr_x0_p_prev_loc_2;                          /* ldr x0, #0xXXXX */
+  uint32_t ldr_x1_p_area_offset_ror;                     /* ldr x1, #0xXXXX */
+  uint32_t str_x1_ptr_x0;                                   /* str x1, [x0] */
+
+  uint32_t ldp_x0_x1;                           /* ldp x0, x1, [sp, #-0xa0] */
+
+  uint32_t b_end;                                          /* skip the data */
+
+  uint64_t area_ptr;
+  uint64_t prev_loc_ptr;
+  uint64_t area_offset;
+  uint64_t area_offset_ror;
+
+  uint8_t end[0];
+
+} afl_log_code_asm_long_t;
+
   #pragma pack(pop)
 
 typedef union {
@@ -85,6 +124,13 @@ typedef union {
 
 } afl_log_code;
 
+typedef union {
+
+  afl_log_code_asm_long_t code;
+  uint8_t                 bytes[0];
+
+} afl_log_code_long;
+
 static const afl_log_code_asm_t template =
     {
 
@@ -119,6 +165,46 @@ static const afl_log_code_asm_t template =
 
 ;
 
+static const afl_log_code_asm_long_t template_long =
+    {.b_imm8 = 0x1400001a,
+
+     .restoration_prolog = 0xa8c947f0,         /* ldp x16, x17, [sp], #0x90 */
+
+     .stp_x0_x1 = 0xa93607e0,                   /* stp x0, x1, [sp, #-0xa0] */
+
+     .ldr_x0_p_prev_loc_1 = 0x58000220,                  /* ldr x0, #0xXXXX */
+     .ldr_x1_ptr_x0 = 0xf9400001,                           /* ldr x1, [x0] */
+
+     .ldr_x0_p_area_offset = 0x58000220,                 /* ldr x0, #0xXXXX */
+     .eor_x0_x1_x0 = 0xca000020,                          /* eor x0, x1, x0 */
+     .ldr_x1_p_area_ptr = 0x58000161,                    /* ldr x1, #0xXXXX */
+     .add_x0_x1_x0 = 0x8b000020,                          /* add x0, x1, x0 */
+
+     .ldrb_w1_x0 = 0x39400001,                             /* ldrb w1, [x0] */
+     .add_w1_w1_1 = 0x11000421,                           /* add w1, w1, #1 */
+     .add_w1_w1_w1_lsr_8 = 0x8b412021,            /* add x1, x1, x1, lsr #8 */
+
+     .strb_w1_ptr_x0 = 0x39000001,                         /* strb w1, [x0] */
+
+     .ldr_x0_p_prev_loc_2 = 0x580000e0,                  /* ldr x0, #0xXXXX */
+     .ldr_x1_p_area_offset_ror = 0x58000141,             /* ldr x1, #0xXXXX */
+     .str_x1_ptr_x0 = 0xf9000001,                           /* str x1, [x0] */
+
+     .ldp_x0_x1 = 0xa97607e0,                   /* ldp x0, x1, [sp, #-0xa0] */
+
+     .b_end = 0x14000009,                                  /* skip the data */
+
+     .area_ptr = 0x0,
+     .prev_loc_ptr = 0x0,
+     .area_offset = 0x0,
+     .area_offset_ror = 0x0,
+
+     .end = {}
+
+}
+
+;
+
 gboolean instrument_is_coverage_optimize_supported(void) {
 
   return true;
@@ -266,16 +352,22 @@ static gboolean instrument_coverage_in_range(gssize offset) {
 
 }
 
-static void instrument_patch_ardp(guint32 *patch, GumAddress insn,
+static bool instrument_patch_ardp(guint32 *patch, GumAddress insn,
                                   GumAddress target) {
 
-  if (!PAGE_ALIGNED(target)) { FATAL("Target not page aligned"); }
+  if (!PAGE_ALIGNED(target)) {
+
+    FWARNF("Target not page aligned");
+    return false;
+
+  }
 
   gssize distance = target - (GUM_ADDRESS(insn) & PAGE_MASK);
   if (!instrument_coverage_in_range(distance)) {
 
-    FATAL("Patch out of range 0x%016lX->0x%016lX = 0x%016lX", insn, target,
-          distance);
+    FVERBOSE("Patch out of range 0x%016lX->0x%016lX = 0x%016lX", insn, target,
+             distance);
+    return false;
 
   }
 
@@ -283,6 +375,95 @@ static void instrument_patch_ardp(guint32 *patch, GumAddress insn,
   guint32 imm_high = ((distance >> 14) & 0x7FFFF) << 5;
   *patch |= imm_low;
   *patch |= imm_high;
+  return true;
+
+}
+
+bool instrument_write_inline(GumArm64Writer *cw, GumAddress code_addr,
+                             guint64 area_offset, gsize area_offset_ror) {
+
+  afl_log_code code = {0};
+  code.code = template;
+
+  /*
+   * Given our map is allocated on a 64KB boundary and our map is a multiple of
+   * 64KB in size, then it should also end on a 64 KB boundary. It is followed
+   * by our previous_pc, so this too should be 64KB aligned.
+   */
+  g_assert(PAGE_ALIGNED(instrument_previous_pc_addr));
+  g_assert(PAGE_ALIGNED(__afl_area_ptr));
+
+  if (!instrument_patch_ardp(
+          &code.code.adrp_x0_prev_loc1,
+          code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc1),
+          GUM_ADDRESS(instrument_previous_pc_addr))) {
+
+    return false;
+
+  }
+
+  code.code.mov_x0_curr_loc |= area_offset << 5;
+
+  if (!instrument_patch_ardp(
+          &code.code.adrp_x1_area_ptr,
+          code_addr + offsetof(afl_log_code, code.adrp_x1_area_ptr),
+          GUM_ADDRESS(__afl_area_ptr))) {
+
+    return false;
+
+  }
+
+  if (!instrument_patch_ardp(
+          &code.code.adrp_x0_prev_loc2,
+          code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc2),
+          GUM_ADDRESS(instrument_previous_pc_addr))) {
+
+    return false;
+
+  }
+
+  code.code.mov_x1_curr_loc_shr_1 |= (area_offset_ror << 5);
+
+  if (instrument_suppress) {
+
+    gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
+
+  } else {
+
+    size_t offset = offsetof(afl_log_code, code.stp_x0_x1);
+    gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
+                               sizeof(afl_log_code) - offset);
+
+  }
+
+  return true;
+
+}
+
+bool instrument_write_inline_long(GumArm64Writer *cw, GumAddress code_addr,
+                                  guint64 area_offset, gsize area_offset_ror) {
+
+  afl_log_code_long code = {0};
+  code.code = template_long;
+
+  code.code.area_ptr = GUM_ADDRESS(__afl_area_ptr);
+  code.code.prev_loc_ptr = GUM_ADDRESS(instrument_previous_pc_addr);
+  code.code.area_offset = area_offset;
+  code.code.area_offset_ror = GUM_ADDRESS(area_offset_ror);
+
+  if (instrument_suppress) {
+
+    gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code_long));
+
+  } else {
+
+    size_t offset = offsetof(afl_log_code_long, code.stp_x0_x1);
+    gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
+                               sizeof(afl_log_code_long) - offset);
+
+  }
+
+  return true;
 
 }
 
@@ -312,6 +493,8 @@ void instrument_coverage_optimize(const cs_insn    *instr,
   }
 
   // gum_arm64_writer_put_brk_imm(cw, 0x0);
+  // uint32_t jmp_dot = 0x14000000;
+  // gum_arm64_writer_put_bytes(cw, (guint8 *)&jmp_dot, sizeof(jmp_dot));
 
   if (instrument_suppress) { instrument_coverage_suppress_init(); }
 
@@ -343,47 +526,19 @@ void instrument_coverage_optimize(const cs_insn    *instr,
 
   }
 
-  code.code = template;
-
-  /*
-   * Given our map is allocated on a 64KB boundary and our map is a multiple of
-   * 64KB in size, then it should also end on a 64 KB boundary. It is followed
-   * by our previous_pc, so this too should be 64KB aligned.
-   */
-  g_assert(PAGE_ALIGNED(instrument_previous_pc_addr));
-  g_assert(PAGE_ALIGNED(__afl_area_ptr));
-
-  instrument_patch_ardp(
-      &code.code.adrp_x0_prev_loc1,
-      code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc1),
-      GUM_ADDRESS(instrument_previous_pc_addr));
-
-  code.code.mov_x0_curr_loc |= area_offset << 5;
-
-  instrument_patch_ardp(
-      &code.code.adrp_x1_area_ptr,
-      code_addr + offsetof(afl_log_code, code.adrp_x1_area_ptr),
-      GUM_ADDRESS(__afl_area_ptr));
-
   map_size_pow2 = util_log2(__afl_map_size);
   area_offset_ror = util_rotate(area_offset, 1, map_size_pow2);
 
-  instrument_patch_ardp(
-      &code.code.adrp_x0_prev_loc2,
-      code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc2),
-      GUM_ADDRESS(instrument_previous_pc_addr));
-
-  code.code.mov_x1_curr_loc_shr_1 |= (area_offset_ror << 5);
+  code.code = template;
 
-  if (instrument_suppress) {
+  if (!instrument_write_inline(cw, code_addr, area_offset, area_offset_ror)) {
 
-    gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
+    if (!instrument_write_inline_long(cw, code_addr, area_offset,
+                                      area_offset_ror)) {
 
-  } else {
+      FATAL("Failed to write inline instrumentation");
 
-    size_t offset = offsetof(afl_log_code, code.stp_x0_x1);
-    gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
-                               sizeof(afl_log_code) - offset);
+    }
 
   }
 
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index c8c50b37..f11c4b25 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -197,7 +197,7 @@ static void afl_print_env(void) {
 
 }
 
-__attribute__((visibility("default"))) void afl_frida_start(void) {
+void afl_frida_config(void) {
 
   FOKF(cRED "**********************");
   FOKF(cRED "* " cYEL "******************" cRED " *");
@@ -225,9 +225,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
 
   js_start();
 
-  /* Initialize */
   output_init();
-
   embedded_init();
   entry_init();
   instrument_init();
@@ -240,12 +238,35 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
   ranges_init();
   stats_init();
 
-  /* Start */
+}
+
+void afl_frida_run(void) {
+
   stalker_start();
   entry_start();
 
 }
 
+__attribute__((visibility("default"))) void afl_frida_start(void) {
+
+  afl_frida_config();
+  afl_frida_run();
+
+}
+
+typedef void *(*entry_func_t)(size_t a1, size_t a2, size_t a3, size_t a4,
+                              size_t a5, size_t a6);
+
+static void *on_entry(size_t a1, size_t a2, size_t a3, size_t a4, size_t a5,
+                      size_t a6) {
+
+  intercept_unhook(GSIZE_TO_POINTER(entry_point));
+  afl_frida_run();
+  entry_func_t entry = (entry_func_t)entry_point;
+  return entry(a1, a2, a3, a4, a5, a6);
+
+}
+
 static int on_main(int argc, char **argv, char **envp) {
 
   int ret;
@@ -254,7 +275,17 @@ static int on_main(int argc, char **argv, char **envp) {
 
   intercept_unhook_self();
 
-  afl_frida_start();
+  afl_frida_config();
+
+  if (entry_point == 0) {
+
+    afl_frida_run();
+
+  } else {
+
+    intercept_hook(GSIZE_TO_POINTER(entry_point), on_entry, NULL);
+
+  }
 
   if (js_main_hook != NULL) {
 
diff --git a/frida_mode/test/testinstr/GNUmakefile b/frida_mode/test/testinstr/GNUmakefile
index ebc0b2dc..46d9b85f 100644
--- a/frida_mode/test/testinstr/GNUmakefile
+++ b/frida_mode/test/testinstr/GNUmakefile
@@ -67,3 +67,8 @@ debug:
 		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
 		--ex 'set disassembly-flavor intel' \
 		--args $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+
+lldb:
+	lldb \
+		-O 'settings set target.env-vars DYLD_INSERT_LIBRARIES=$(ROOT)afl-frida-trace.so' \
+		-- $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc
index d7b03634..2d17ffd4 100644
--- a/instrumentation/SanitizerCoverageLTO.so.cc
+++ b/instrumentation/SanitizerCoverageLTO.so.cc
@@ -331,7 +331,7 @@ llvmGetPassPluginInfo() {
 #if LLVM_VERSION_MAJOR <= 13
             using OptimizationLevel = typename PassBuilder::OptimizationLevel;
 #endif
-#if LLVM_VERSION_MAJOR >= 15
+#if LLVM_VERSION_MAJOR >= 16
             PB.registerFullLinkTimeOptimizationLastEPCallback(
 #else
             PB.registerOptimizerLastEPCallback(
diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc
index 8fed2042..20f54b84 100644
--- a/instrumentation/SanitizerCoveragePCGUARD.so.cc
+++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc
@@ -13,42 +13,64 @@
 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
+#if LLVM_VERSION_MAJOR >= 15
+  #if LLVM_VERSION_MAJOR < 17
+    #include "llvm/ADT/Triple.h"
+  #endif
+#endif
 #if LLVM_VERSION_MAJOR < 17
-  #include "llvm/ADT/Triple.h"
   #include "llvm/Analysis/EHPersonalities.h"
-#else
-  #include "llvm/IR/EHPersonalities.h"
 #endif
 #include "llvm/Analysis/PostDominators.h"
-#include "llvm/IR/CFG.h"
+#if LLVM_VERSION_MAJOR < 15
+  #include "llvm/IR/CFG.h"
+#endif
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/DataLayout.h"
-#include "llvm/IR/DebugInfo.h"
+#if LLVM_VERSION_MAJOR < 15
+  #include "llvm/IR/DebugInfo.h"
+#endif
 #include "llvm/IR/Dominators.h"
+#if LLVM_VERSION_MAJOR >= 17
+  #include "llvm/Analysis/EHPersonalities.h"
+#endif
 #include "llvm/IR/Function.h"
-#include "llvm/IR/GlobalVariable.h"
+#if LLVM_VERSION_MAJOR >= 16
+  #include "llvm/IR/GlobalVariable.h"
+#endif
 #include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/InlineAsm.h"
+#if LLVM_VERSION_MAJOR < 15
+  #include "llvm/IR/InlineAsm.h"
+#endif
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/LLVMContext.h"
-#include "llvm/IR/MDBuilder.h"
-#include "llvm/IR/Mangler.h"
+#if LLVM_VERSION_MAJOR < 15
+  #include "llvm/IR/MDBuilder.h"
+  #include "llvm/IR/Mangler.h"
+#endif
 #include "llvm/IR/Module.h"
 #include "llvm/IR/PassManager.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
 #include "llvm/IR/Type.h"
-#include "llvm/InitializePasses.h"
+#if LLVM_VERSION_MAJOR < 17
+  #include "llvm/InitializePasses.h"
+#endif
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/SpecialCaseList.h"
 #include "llvm/Support/VirtualFileSystem.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/Instrumentation.h"
+#if LLVM_VERSION_MAJOR < 15
+  #include "llvm/Support/raw_ostream.h"
+#endif
+#if LLVM_VERSION_MAJOR < 17
+  #include "llvm/Transforms/Instrumentation.h"
+#else
+  #include "llvm/TargetParser/Triple.h"
+#endif
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Transforms/Utils/ModuleUtils.h"
-#include "llvm/Passes/PassPlugin.h"
-#include "llvm/Passes/PassBuilder.h"
-#include "llvm/IR/PassManager.h"
 
 #include "config.h"
 #include "debug.h"
@@ -58,7 +80,8 @@ using namespace llvm;
 
 #define DEBUG_TYPE "sancov"
 
-const char SanCovTracePCIndirName[] = "__sanitizer_cov_trace_pc_indir";
+static const uint64_t SanCtorAndDtorPriority = 2;
+
 const char SanCovTracePCName[] = "__sanitizer_cov_trace_pc";
 const char SanCovTraceCmp1[] = "__sanitizer_cov_trace_cmp1";
 const char SanCovTraceCmp2[] = "__sanitizer_cov_trace_cmp2";
@@ -68,22 +91,13 @@ const char SanCovTraceConstCmp1[] = "__sanitizer_cov_trace_const_cmp1";
 const char SanCovTraceConstCmp2[] = "__sanitizer_cov_trace_const_cmp2";
 const char SanCovTraceConstCmp4[] = "__sanitizer_cov_trace_const_cmp4";
 const char SanCovTraceConstCmp8[] = "__sanitizer_cov_trace_const_cmp8";
-const char SanCovTraceDiv4[] = "__sanitizer_cov_trace_div4";
-const char SanCovTraceDiv8[] = "__sanitizer_cov_trace_div8";
-const char SanCovTraceGep[] = "__sanitizer_cov_trace_gep";
 const char SanCovTraceSwitchName[] = "__sanitizer_cov_trace_switch";
+
 const char SanCovModuleCtorTracePcGuardName[] =
     "sancov.module_ctor_trace_pc_guard";
-const char SanCovModuleCtor8bitCountersName[] =
-    "sancov.module_ctor_8bit_counters";
-const char SanCovModuleCtorBoolFlagName[] = "sancov.module_ctor_bool_flag";
-static const uint64_t SanCtorAndDtorPriority = 2;
+const char SanCovTracePCGuardInitName[] = "__sanitizer_cov_trace_pc_guard_init";
 
 const char SanCovTracePCGuardName[] = "__sanitizer_cov_trace_pc_guard";
-const char SanCovTracePCGuardInitName[] = "__sanitizer_cov_trace_pc_guard_init";
-const char SanCov8bitCountersInitName[] = "__sanitizer_cov_8bit_counters_init";
-const char SanCovBoolFlagInitName[] = "__sanitizer_cov_bool_flag_init";
-const char SanCovPCsInitName[] = "__sanitizer_cov_pcs_init";
 
 const char SanCovGuardsSectionName[] = "sancov_guards";
 const char SanCovCountersSectionName[] = "sancov_cntrs";
@@ -99,27 +113,9 @@ namespace {
 
 SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
 
-  // Sets CoverageType and IndirectCalls.
-  // SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel);
-  Options.CoverageType =
-      SanitizerCoverageOptions::SCK_Edge;  // std::max(Options.CoverageType,
-                                           // CLOpts.CoverageType);
-  Options.IndirectCalls = false;           // CLOpts.IndirectCalls;
-  Options.TraceCmp = false;                //|= ClCMPTracing;
-  Options.TraceDiv = false;                //|= ClDIVTracing;
-  Options.TraceGep = false;                //|= ClGEPTracing;
-  Options.TracePC = false;                 //|= ClTracePC;
-  Options.TracePCGuard = true;             // |= ClTracePCGuard;
-  Options.Inline8bitCounters = 0;          //|= ClInline8bitCounters;
-  // Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag;
-  Options.PCTable = false;     //|= ClCreatePCTable;
-  Options.NoPrune = false;     //|= !ClPruneBlocks;
-  Options.StackDepth = false;  //|= ClStackDepth;
-  if (!Options.TracePCGuard && !Options.TracePC &&
-      !Options.Inline8bitCounters && !Options.StackDepth /*&&
-      !Options.InlineBoolFlag*/)
-    Options.TracePCGuard = true;  // TracePCGuard is default.
-
+  Options.CoverageType = SanitizerCoverageOptions::SCK_Edge;
+  // Options.NoPrune = true;
+  Options.TracePCGuard = true;  // TracePCGuard is default.
   return Options;
 
 }
@@ -139,20 +135,13 @@ class ModuleSanitizerCoverageAFL
   }
 
   PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
-
-  bool instrumentModule(Module &M, DomTreeCallback DTCallback,
-                        PostDomTreeCallback PDTCallback);
+  bool              instrumentModule(Module &M, DomTreeCallback DTCallback,
+                                     PostDomTreeCallback PDTCallback);
 
  private:
   void instrumentFunction(Function &F, DomTreeCallback DTCallback,
                           PostDomTreeCallback PDTCallback);
-  void InjectCoverageForIndirectCalls(Function               &F,
-                                      ArrayRef<Instruction *> IndirCalls);
   void InjectTraceForCmp(Function &F, ArrayRef<Instruction *> CmpTraceTargets);
-  void InjectTraceForDiv(Function                  &F,
-                         ArrayRef<BinaryOperator *> DivTraceTargets);
-  void InjectTraceForGep(Function                     &F,
-                         ArrayRef<GetElementPtrInst *> GepTraceTargets);
   void InjectTraceForSwitch(Function               &F,
                             ArrayRef<Instruction *> SwitchTraceTargets);
   bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks,
@@ -173,20 +162,21 @@ class ModuleSanitizerCoverageAFL
 
   void SetNoSanitizeMetadata(Instruction *I) {
 
+#if LLVM_VERSION_MAJOR >= 16
+    I->setMetadata(LLVMContext::MD_nosanitize, MDNode::get(*C, std::nullopt));
+#else
     I->setMetadata(I->getModule()->getMDKindID("nosanitize"),
                    MDNode::get(*C, None));
+#endif
 
   }
 
   std::string     getSectionName(const std::string &Section) const;
   std::string     getSectionStart(const std::string &Section) const;
   std::string     getSectionEnd(const std::string &Section) const;
-  FunctionCallee  SanCovTracePCIndir;
   FunctionCallee  SanCovTracePC, SanCovTracePCGuard;
   FunctionCallee  SanCovTraceCmpFunction[4];
   FunctionCallee  SanCovTraceConstCmpFunction[4];
-  FunctionCallee  SanCovTraceDivFunction[2];
-  FunctionCallee  SanCovTraceGepFunction;
   FunctionCallee  SanCovTraceSwitchFunction;
   GlobalVariable *SanCovLowestStack;
   Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy,
@@ -215,18 +205,16 @@ class ModuleSanitizerCoverageAFL
 
 }  // namespace
 
-#if LLVM_VERSION_MAJOR >= 11                        /* use new pass manager */
-
 extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
 llvmGetPassPluginInfo() {
 
-  return {LLVM_PLUGIN_API_VERSION, "SanitizerCoveragePCGUARD", "v0.1",
+  return {LLVM_PLUGIN_API_VERSION, "SanitizerCoveragePCGUARD", "v0.2",
           /* lambda to insert our pass into the pass pipeline. */
           [](PassBuilder &PB) {
 
-  #if LLVM_VERSION_MAJOR <= 13
+#if LLVM_VERSION_MAJOR == 13
             using OptimizationLevel = typename PassBuilder::OptimizationLevel;
-  #endif
+#endif
             PB.registerOptimizerLastEPCallback(
                 [](ModulePassManager &MPM, OptimizationLevel OL) {
 
@@ -238,8 +226,7 @@ llvmGetPassPluginInfo() {
 
 }
 
-#endif
-
+#if LLVM_VERSION_MAJOR == 1
 PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module                &M,
                                                   ModuleAnalysisManager &MAM) {
 
@@ -257,34 +244,65 @@ PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module                &M,
 
   };
 
+  if (!ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
+    return PreservedAnalyses::all();
+
+  PreservedAnalyses PA = PreservedAnalyses::none();
+  // GlobalsAA is considered stateless and does not get invalidated unless
+  // explicitly invalidated; PreservedAnalyses::none() is not enough. Sanitizers
+  // make changes that require GlobalsAA to be invalidated.
+  PA.abandon<GlobalsAA>();
+  return PA;
+
+}
+
+#else
+  #if LLVM_VERSION_MAJOR >= 16
+PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
+                                                  ModuleAnalysisManager &MAM) {
+
+  #else
+PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module                &M,
+                                                  ModuleAnalysisManager &MAM) {
+
+  #endif
+  ModuleSanitizerCoverageAFL ModuleSancov(Options);
+  auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
+  auto DTCallback = [&FAM](Function &F) -> const DominatorTree * {
+
+    return &FAM.getResult<DominatorTreeAnalysis>(F);
+
+  };
+
+  auto PDTCallback = [&FAM](Function &F) -> const PostDominatorTree * {
+
+    return &FAM.getResult<PostDominatorTreeAnalysis>(F);
+
+  };
+
   if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
     return PreservedAnalyses::none();
   return PreservedAnalyses::all();
 
 }
 
+#endif
+
 std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd(
     Module &M, const char *Section, Type *Ty) {
 
-  GlobalVariable *SecStart =
-      new GlobalVariable(M,
-#if LLVM_VERSION_MAJOR >= 15
-                         Ty,
-#else
-                         Ty->getPointerElementType(),
-#endif
-                         false, GlobalVariable::ExternalWeakLinkage, nullptr,
-                         getSectionStart(Section));
+  // Use ExternalWeak so that if all sections are discarded due to section
+  // garbage collection, the linker will not report undefined symbol errors.
+  // Windows defines the start/stop symbols in compiler-rt so no need for
+  // ExternalWeak.
+  GlobalValue::LinkageTypes Linkage = TargetTriple.isOSBinFormatCOFF()
+                                          ? GlobalVariable::ExternalLinkage
+                                          : GlobalVariable::ExternalWeakLinkage;
+  GlobalVariable *SecStart = new GlobalVariable(M, Ty, false, Linkage, nullptr,
+                                                getSectionStart(Section));
   SecStart->setVisibility(GlobalValue::HiddenVisibility);
-  GlobalVariable *SecEnd =
-      new GlobalVariable(M,
-#if LLVM_VERSION_MAJOR >= 15
-                         Ty,
-#else
-                         Ty->getPointerElementType(),
-#endif
-                         false, GlobalVariable::ExternalWeakLinkage, nullptr,
-                         getSectionEnd(Section));
+  GlobalVariable *SecEnd = new GlobalVariable(M, Ty, false, Linkage, nullptr,
+                                              getSectionEnd(Section));
   SecEnd->setVisibility(GlobalValue::HiddenVisibility);
   IRBuilder<> IRB(M.getContext());
   if (!TargetTriple.isOSBinFormatCOFF())
@@ -295,7 +313,8 @@ std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd(
   auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy);
   auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr,
                            ConstantInt::get(IntptrTy, sizeof(uint64_t)));
-  return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEnd);
+  return std::make_pair(IRB.CreatePointerCast(GEP, PointerType::getUnqual(Ty)),
+                        SecEnd);
 
 }
 
@@ -307,8 +326,9 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections(
   auto      SecStart = SecStartEnd.first;
   auto      SecEnd = SecStartEnd.second;
   Function *CtorFunc;
+  Type     *PtrTy = PointerType::getUnqual(Ty);
   std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions(
-      M, CtorName, InitFunctionName, {Ty, Ty}, {SecStart, SecEnd});
+      M, CtorName, InitFunctionName, {PtrTy, PtrTy}, {SecStart, SecEnd});
   assert(CtorFunc->getName() == CtorName);
 
   if (TargetTriple.supportsCOMDAT()) {
@@ -332,7 +352,6 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections(
     // to include the sancov constructor. This way the linker can deduplicate
     // the constructors but always leave one copy.
     CtorFunc->setLinkage(GlobalValue::WeakODRLinkage);
-    appendToUsed(M, CtorFunc);
 
   }
 
@@ -344,37 +363,25 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
     Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
 
   setvbuf(stdout, NULL, _IONBF, 0);
-  if (getenv("AFL_DEBUG")) debug = 1;
+
+  if (getenv("AFL_DEBUG")) { debug = 1; }
 
   if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
 
     SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n");
 
-  } else
+  } else {
 
     be_quiet = 1;
 
+  }
+
   skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
   use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST");
 
   initInstrumentList();
   scanForDangerousFunctions(&M);
 
-  if (debug) {
-
-    fprintf(stderr,
-            "SANCOV: covtype:%u indirect:%d stack:%d noprune:%d "
-            "createtable:%d tracepcguard:%d tracepc:%d\n",
-            Options.CoverageType, Options.IndirectCalls == true ? 1 : 0,
-            Options.StackDepth == true ? 1 : 0, Options.NoPrune == true ? 1 : 0,
-            // Options.InlineBoolFlag == true ? 1 : 0,
-            Options.PCTable == true ? 1 : 0,
-            Options.TracePCGuard == true ? 1 : 0,
-            Options.TracePC == true ? 1 : 0);
-
-  }
-
-  if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false;
   C = &(M.getContext());
   DL = &M.getDataLayout();
   CurModule = &M;
@@ -397,16 +404,14 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
   Int16Ty = IRB.getInt16Ty();
   Int8Ty = IRB.getInt8Ty();
   Int1Ty = IRB.getInt1Ty();
-  LLVMContext &Ctx = M.getContext();
 
+  LLVMContext &Ctx = M.getContext();
   AFLMapPtr =
       new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
                          GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
   One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1);
   Zero = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 0);
 
-  SanCovTracePCIndir =
-      M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy);
   // Make sure smaller parameters are zero-extended to i64 if required by the
   // target ABI.
   AttributeList SanCovTraceCmpZeroExtAL;
@@ -436,26 +441,13 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
   SanCovTraceConstCmpFunction[3] =
       M.getOrInsertFunction(SanCovTraceConstCmp8, VoidTy, Int64Ty, Int64Ty);
 
-  {
-
-    AttributeList AL;
-    AL = AL.addParamAttribute(*C, 0, Attribute::ZExt);
-    SanCovTraceDivFunction[0] =
-        M.getOrInsertFunction(SanCovTraceDiv4, AL, VoidTy, IRB.getInt32Ty());
-
-  }
-
-  SanCovTraceDivFunction[1] =
-      M.getOrInsertFunction(SanCovTraceDiv8, VoidTy, Int64Ty);
-  SanCovTraceGepFunction =
-      M.getOrInsertFunction(SanCovTraceGep, VoidTy, IntptrTy);
   SanCovTraceSwitchFunction =
       M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy);
 
   Constant *SanCovLowestStackConstant =
       M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy);
   SanCovLowestStack = dyn_cast<GlobalVariable>(SanCovLowestStackConstant);
-  if (!SanCovLowestStack) {
+  if (!SanCovLowestStack || SanCovLowestStack->getValueType() != IntptrTy) {
 
     C->emitError(StringRef("'") + SanCovLowestStackName +
                  "' should not be declared by the user");
@@ -465,8 +457,6 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
 
   SanCovLowestStack->setThreadLocalMode(
       GlobalValue::ThreadLocalMode::InitialExecTLSModel);
-  if (Options.StackDepth && !SanCovLowestStack->isDeclaration())
-    SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy));
 
   SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy);
   SanCovTracePCGuard =
@@ -481,40 +471,25 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
     Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName,
                                       SanCovTracePCGuardInitName, Int32PtrTy,
                                       SanCovGuardsSectionName);
-  if (Function8bitCounterArray)
-    Ctor = CreateInitCallsForSections(M, SanCovModuleCtor8bitCountersName,
-                                      SanCov8bitCountersInitName, Int8PtrTy,
-                                      SanCovCountersSectionName);
-  if (FunctionBoolArray) {
 
-    Ctor = CreateInitCallsForSections(M, SanCovModuleCtorBoolFlagName,
-                                      SanCovBoolFlagInitName, Int1PtrTy,
-                                      SanCovBoolFlagSectionName);
+  if (Ctor && debug) {
 
-  }
-
-  if (Ctor && Options.PCTable) {
-
-    auto SecStartEnd = CreateSecStartEnd(M, SanCovPCsSectionName, IntptrPtrTy);
-    FunctionCallee InitFunction = declareSanitizerInitFunction(
-        M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy});
-    IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator());
-    IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second});
+    fprintf(stderr, "SANCOV: installed pcguard_init in ctor\n");
 
   }
 
-  // We don't reference these arrays directly in any of our runtime functions,
-  // so we need to prevent them from being dead stripped.
-  if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed);
+  appendToUsed(M, GlobalsToAppendToUsed);
   appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed);
 
   if (!be_quiet) {
 
-    if (!instr)
+    if (!instr) {
+
       WARNF("No instrumentation targets found.");
-    else {
 
-      char modeline[100];
+    } else {
+
+      char modeline[128];
       snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s",
                getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
                getenv("AFL_USE_ASAN") ? ", ASAN" : "",
@@ -535,39 +510,36 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
 }
 
 // True if block has successors and it dominates all of them.
-bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) {
+static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) {
 
-  if (succ_begin(BB) == succ_end(BB)) return false;
+  if (succ_empty(BB)) return false;
 
-  for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) {
+  return llvm::all_of(successors(BB), [&](const BasicBlock *SUCC) {
 
-    if (!DT->dominates(BB, SUCC)) return false;
+    return DT->dominates(BB, SUCC);
 
-  }
-
-  return true;
+  });
 
 }
 
 // True if block has predecessors and it postdominates all of them.
-bool isFullPostDominator(const BasicBlock *BB, const PostDominatorTree *PDT) {
+static bool isFullPostDominator(const BasicBlock        *BB,
+                                const PostDominatorTree *PDT) {
 
-  if (pred_begin(BB) == pred_end(BB)) return false;
+  if (pred_empty(BB)) return false;
 
-  for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) {
+  return llvm::all_of(predecessors(BB), [&](const BasicBlock *PRED) {
 
-    if (!PDT->dominates(BB, PRED)) return false;
+    return PDT->dominates(BB, PRED);
 
-  }
-
-  return true;
+  });
 
 }
 
-bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
-                           const DominatorTree            *DT,
-                           const PostDominatorTree        *PDT,
-                           const SanitizerCoverageOptions &Options) {
+static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
+                                  const DominatorTree            *DT,
+                                  const PostDominatorTree        *PDT,
+                                  const SanitizerCoverageOptions &Options) {
 
   // Don't insert coverage for blocks containing nothing but unreachable: we
   // will never call __sanitizer_cov() for them, so counting them in
@@ -582,10 +554,6 @@ bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
 
   if (Options.NoPrune || &F.getEntryBlock() == BB) return true;
 
-  if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function &&
-      &F.getEntryBlock() != BB)
-    return false;
-
   // Do not instrument full dominators, or full post-dominators with multiple
   // predecessors.
   return !isFullDominator(BB, DT) &&
@@ -597,38 +565,47 @@ bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
 // A twist here is that we treat From->To as a backedge if
 //   * To dominates From or
 //   * To->UniqueSuccessor dominates From
-bool IsBackEdge(BasicBlock *From, BasicBlock *To, const DominatorTree *DT) {
+#if 0
+static bool IsBackEdge(BasicBlock *From, BasicBlock *To,
+                       const DominatorTree *DT) {
 
-  if (DT->dominates(To, From)) return true;
+  if (DT->dominates(To, From))
+    return true;
   if (auto Next = To->getUniqueSuccessor())
-    if (DT->dominates(Next, From)) return true;
+    if (DT->dominates(Next, From))
+      return true;
   return false;
 
 }
 
+#endif
+
 // Prunes uninteresting Cmp instrumentation:
 //   * CMP instructions that feed into loop backedge branch.
 //
 // Note that Cmp pruning is controlled by the same flag as the
 // BB pruning.
-bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT,
-                      const SanitizerCoverageOptions &Options) {
+#if 0
+static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT,
+                             const SanitizerCoverageOptions &Options) {
 
   if (!Options.NoPrune)
     if (CMP->hasOneUse())
       if (auto BR = dyn_cast<BranchInst>(CMP->user_back()))
         for (BasicBlock *B : BR->successors())
-          if (IsBackEdge(BR->getParent(), B, DT)) return false;
+          if (IsBackEdge(BR->getParent(), B, DT))
+            return false;
   return true;
 
 }
 
+#endif
+
 void ModuleSanitizerCoverageAFL::instrumentFunction(
     Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
 
   if (F.empty()) return;
   if (!isInInstrumentList(&F, FMNAME)) return;
-
   if (F.getName().find(".module_ctor") != std::string::npos)
     return;  // Should not instrument sanitizer init functions.
   if (F.getName().startswith("__sanitizer_"))
@@ -647,15 +624,13 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
   if (F.hasPersonalityFn() &&
       isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
     return;
+  if (F.hasFnAttribute(Attribute::NoSanitizeCoverage)) return;
   if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
     SplitAllCriticalEdges(
         F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests());
-  SmallVector<Instruction *, 8>       IndirCalls;
-  SmallVector<BasicBlock *, 16>       BlocksToInstrument;
-  SmallVector<Instruction *, 8>       CmpTraceTargets;
-  SmallVector<Instruction *, 8>       SwitchTraceTargets;
-  SmallVector<BinaryOperator *, 8>    DivTraceTargets;
-  SmallVector<GetElementPtrInst *, 8> GepTraceTargets;
+  SmallVector<BasicBlock *, 16> BlocksToInstrument;
+  SmallVector<Instruction *, 8> CmpTraceTargets;
+  SmallVector<Instruction *, 8> SwitchTraceTargets;
 
   const DominatorTree     *DT = DTCallback(F);
   const PostDominatorTree *PDT = PDTCallback(F);
@@ -665,47 +640,28 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
 
     if (shouldInstrumentBlock(F, &BB, DT, PDT, Options))
       BlocksToInstrument.push_back(&BB);
-    for (auto &Inst : BB) {
-
-      if (Options.IndirectCalls) {
-
-        CallBase *CB = dyn_cast<CallBase>(&Inst);
-        if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst);
-
-      }
+    /*
+        for (auto &Inst : BB) {
 
-      if (Options.TraceCmp) {
+          if (Options.TraceCmp) {
 
-        if (ICmpInst *CMP = dyn_cast<ICmpInst>(&Inst))
-          if (IsInterestingCmp(CMP, DT, Options))
-            CmpTraceTargets.push_back(&Inst);
-        if (isa<SwitchInst>(&Inst)) SwitchTraceTargets.push_back(&Inst);
+            if (ICmpInst *CMP = dyn_cast<ICmpInst>(&Inst))
+              if (IsInterestingCmp(CMP, DT, Options))
+                CmpTraceTargets.push_back(&Inst);
+            if (isa<SwitchInst>(&Inst))
+              SwitchTraceTargets.push_back(&Inst);
 
-      }
+          }
 
-      if (Options.TraceDiv)
-        if (BinaryOperator *BO = dyn_cast<BinaryOperator>(&Inst))
-          if (BO->getOpcode() == Instruction::SDiv ||
-              BO->getOpcode() == Instruction::UDiv)
-            DivTraceTargets.push_back(BO);
-      if (Options.TraceGep)
-        if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(&Inst))
-          GepTraceTargets.push_back(GEP);
-      if (Options.StackDepth)
-        if (isa<InvokeInst>(Inst) ||
-            (isa<CallInst>(Inst) && !isa<IntrinsicInst>(Inst)))
-          IsLeafFunc = false;
+        }
 
-    }
+    */
 
   }
 
   InjectCoverage(F, BlocksToInstrument, IsLeafFunc);
-  InjectCoverageForIndirectCalls(F, IndirCalls);
-  InjectTraceForCmp(F, CmpTraceTargets);
-  InjectTraceForSwitch(F, SwitchTraceTargets);
-  InjectTraceForDiv(F, DivTraceTargets);
-  InjectTraceForGep(F, GepTraceTargets);
+  // InjectTraceForCmp(F, CmpTraceTargets);
+  // InjectTraceForSwitch(F, SwitchTraceTargets);
 
 }
 
@@ -717,33 +673,30 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection(
       *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage,
       Constant::getNullValue(ArrayTy), "__sancov_gen_");
 
-#if LLVM_VERSION_MAJOR >= 13
   if (TargetTriple.supportsCOMDAT() &&
       (TargetTriple.isOSBinFormatELF() || !F.isInterposable()))
     if (auto Comdat = getOrCreateFunctionComdat(F, TargetTriple))
       Array->setComdat(Comdat);
-#else
-  if (TargetTriple.supportsCOMDAT() && !F.isInterposable())
-    if (auto Comdat =
-            GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId))
-      Array->setComdat(Comdat);
-#endif
-
   Array->setSection(getSectionName(Section));
-#if (LLVM_VERSION_MAJOR >= 11) || \
-    (LLVM_VERSION_MAJOR == 10 && LLVM_VERSION_MINOR >= 1)
-  #if LLVM_VERSION_MAJOR >= 16
+#if LLVM_VERSION_MAJOR >= 16
   Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue()));
-  #else
-  Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize()));
-  #endif
 #else
-  Array->setAlignment(Align(4));  // cheating
+  Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize()));
 #endif
-  GlobalsToAppendToUsed.push_back(Array);
-  GlobalsToAppendToCompilerUsed.push_back(Array);
-  MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F));
-  Array->addMetadata(LLVMContext::MD_associated, *MD);
+
+  // sancov_pcs parallels the other metadata section(s). Optimizers (e.g.
+  // GlobalOpt/ConstantMerge) may not discard sancov_pcs and the other
+  // section(s) as a unit, so we conservatively retain all unconditionally in
+  // the compiler.
+  //
+  // With comdat (COFF/ELF), the linker can guarantee the associated sections
+  // will be retained or discarded as a unit, so llvm.compiler.used is
+  // sufficient. Otherwise, conservatively make all of them retained by the
+  // linker.
+  if (Array->hasComdat())
+    GlobalsToAppendToCompilerUsed.push_back(Array);
+  else
+    GlobalsToAppendToUsed.push_back(Array);
 
   return Array;
 
@@ -768,8 +721,12 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreatePCArray(
 
       PCs.push_back((Constant *)IRB.CreatePointerCast(
           BlockAddress::get(AllBlocks[i]), IntptrPtrTy));
+#if LLVM_VERSION_MAJOR >= 16
+      PCs.push_back(Constant::getNullValue(IntptrPtrTy));
+#else
       PCs.push_back((Constant *)IRB.CreateIntToPtr(
           ConstantInt::get(IntptrTy, 0), IntptrPtrTy));
+#endif
 
     }
 
@@ -792,21 +749,13 @@ void ModuleSanitizerCoverageAFL::CreateFunctionLocalArrays(
     FunctionGuardArray = CreateFunctionLocalArrayInSection(
         AllBlocks.size() + special, F, Int32Ty, SanCovGuardsSectionName);
 
-  if (Options.Inline8bitCounters)
-    Function8bitCounterArray = CreateFunctionLocalArrayInSection(
-        AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName);
-  /*
-    if (Options.InlineBoolFlag)
-      FunctionBoolArray = CreateFunctionLocalArrayInSection(
-          AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName);
-  */
-  if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks);
-
 }
 
 bool ModuleSanitizerCoverageAFL::InjectCoverage(
     Function &F, ArrayRef<BasicBlock *> AllBlocks, bool IsLeafFunc) {
 
+  if (AllBlocks.empty()) return false;
+
   uint32_t        cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0;
   static uint32_t first = 1;
 
@@ -855,7 +804,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
 
         }
 
-#if (LLVM_VERSION_MAJOR >= 12)
         else if (t->getTypeID() == llvm::Type::FixedVectorTyID) {
 
           FixedVectorType *tt = dyn_cast<FixedVectorType>(t);
@@ -868,16 +816,14 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
 
         }
 
-#endif
-
       }
 
     }
 
   }
 
-  /* Create PCGUARD array */
   CreateFunctionLocalArrays(F, AllBlocks, first + cnt_cov + cnt_sel_inc);
+
   if (first) { first = 0; }
   selects += cnt_sel;
 
@@ -889,12 +835,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
 
       CallInst *callInst = nullptr;
 
-      /*
-                                std::string errMsg;
-                                raw_string_ostream os(errMsg);
-                            IN.print(os);
-                            fprintf(stderr, "X: %s\n", os.str().c_str());
-      */
       if ((callInst = dyn_cast<CallInst>(&IN))) {
 
         Function *Callee = callInst->getCalledFunction();
@@ -1033,12 +973,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
 
               }
 
-              /*
-                          std::string errMsg;
-                          raw_string_ostream os(errMsg);
-                      x->print(os);
-                      fprintf(stderr, "X: %s\n", os.str().c_str());
-              */
               result = IRB.CreateSelect(condition, x, y);
 
             }
@@ -1063,13 +997,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
             IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr);
         ModuleSanitizerCoverageAFL::SetNoSanitizeMetadata(MapPtr);
 
-        /*
-                    std::string errMsg;
-                    raw_string_ostream os(errMsg);
-                    result->print(os);
-                    fprintf(stderr, "X: %s\n", os.str().c_str());
-        */
-
         while (1) {
 
           /* Get CurLoc */
@@ -1159,29 +1086,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
 
 }
 
-// On every indirect call we call a run-time function
-// __sanitizer_cov_indir_call* with two parameters:
-//   - callee address,
-//   - global cache array that contains CacheSize pointers (zero-initialized).
-//     The cache is used to speed up recording the caller-callee pairs.
-// The address of the caller is passed implicitly via caller PC.
-// CacheSize is encoded in the name of the run-time function.
-void ModuleSanitizerCoverageAFL::InjectCoverageForIndirectCalls(
-    Function &F, ArrayRef<Instruction *> IndirCalls) {
-
-  if (IndirCalls.empty()) return;
-  for (auto I : IndirCalls) {
-
-    IRBuilder<> IRB(I);
-    CallBase   &CB = cast<CallBase>(*I);
-    Value      *Callee = CB.getCalledOperand();
-    if (isa<InlineAsm>(Callee)) continue;
-    IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy));
-
-  }
-
-}
-
 // For every switch statement we insert a call:
 // __sanitizer_cov_trace_switch(CondValue,
 //      {NumCases, ValueSizeInBits, Case0Value, Case1Value, Case2Value, ... })
@@ -1237,41 +1141,6 @@ void ModuleSanitizerCoverageAFL::InjectTraceForSwitch(
 
 }
 
-void ModuleSanitizerCoverageAFL::InjectTraceForDiv(
-    Function &, ArrayRef<BinaryOperator *> DivTraceTargets) {
-
-  for (auto BO : DivTraceTargets) {
-
-    IRBuilder<> IRB(BO);
-    Value      *A1 = BO->getOperand(1);
-    if (isa<ConstantInt>(A1)) continue;
-    if (!A1->getType()->isIntegerTy()) continue;
-    uint64_t TypeSize = DL->getTypeStoreSizeInBits(A1->getType());
-    int      CallbackIdx = TypeSize == 32 ? 0 : TypeSize == 64 ? 1 : -1;
-    if (CallbackIdx < 0) continue;
-    auto Ty = Type::getIntNTy(*C, TypeSize);
-    IRB.CreateCall(SanCovTraceDivFunction[CallbackIdx],
-                   {IRB.CreateIntCast(A1, Ty, true)});
-
-  }
-
-}
-
-void ModuleSanitizerCoverageAFL::InjectTraceForGep(
-    Function &, ArrayRef<GetElementPtrInst *> GepTraceTargets) {
-
-  for (auto GEP : GepTraceTargets) {
-
-    IRBuilder<> IRB(GEP);
-    for (Use &Idx : GEP->indices())
-      if (!isa<ConstantInt>(Idx) && Idx->getType()->isIntegerTy())
-        IRB.CreateCall(SanCovTraceGepFunction,
-                       {IRB.CreateIntCast(Idx, IntptrTy, true)});
-
-  }
-
-}
-
 void ModuleSanitizerCoverageAFL::InjectTraceForCmp(
     Function &, ArrayRef<Instruction *> CmpTraceTargets) {
 
@@ -1321,27 +1190,44 @@ void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function   &F,
 
   BasicBlock::iterator IP = BB.getFirstInsertionPt();
   bool                 IsEntryBB = &BB == &F.getEntryBlock();
+  DebugLoc             EntryLoc;
 
   if (IsEntryBB) {
 
-    // Keep allocas and llvm.localescape calls in the entry block.  Even
+    if (auto SP = F.getSubprogram())
+      EntryLoc = DILocation::get(SP->getContext(), SP->getScopeLine(), 0, SP);
+    // Keep static allocas and llvm.localescape calls in the entry block.  Even
     // if we aren't splitting the block, it's nice for allocas to be before
     // calls.
     IP = PrepareToSplitEntryBlock(BB, IP);
+#if LLVM_VERSION_MAJOR < 15
 
-  }
-
-  IRBuilder<> IRB(&*IP);
-
-  if (Options.TracePC) {
+  } else {
 
-    IRB.CreateCall(SanCovTracePC);
-    //        ->setCannotMerge();  // gets the PC using GET_CALLER_PC.
+    EntryLoc = IP->getDebugLoc();
+    if (!EntryLoc)
+      if (auto *SP = F.getSubprogram())
+        EntryLoc = DILocation::get(SP->getContext(), 0, 0, SP);
+#endif
 
   }
 
+#if LLVM_VERSION_MAJOR >= 16
+  InstrumentationIRBuilder IRB(&*IP);
+#else
+  IRBuilder<> IRB(&*IP);
+#endif
+  if (EntryLoc) IRB.SetCurrentDebugLocation(EntryLoc);
   if (Options.TracePCGuard) {
 
+    /*
+      auto GuardPtr = IRB.CreateIntToPtr(
+          IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy),
+                        ConstantInt::get(IntptrTy, Idx * 4)),
+          Int32PtrTy);
+      IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge();
+    */
+
     /* Get CurLoc */
 
     Value *GuardPtr = IRB.CreateIntToPtr(
@@ -1399,57 +1285,6 @@ void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function   &F,
 
   }
 
-  if (Options.Inline8bitCounters) {
-
-    auto CounterPtr = IRB.CreateGEP(
-        Function8bitCounterArray->getValueType(), Function8bitCounterArray,
-        {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)});
-    auto Load = IRB.CreateLoad(Int8Ty, CounterPtr);
-    auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1));
-    auto Store = IRB.CreateStore(Inc, CounterPtr);
-    SetNoSanitizeMetadata(Load);
-    SetNoSanitizeMetadata(Store);
-
-  }
-
-  /*
-    if (Options.InlineBoolFlag) {
-
-      auto FlagPtr = IRB.CreateGEP(
-          FunctionBoolArray->getValueType(), FunctionBoolArray,
-          {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)});
-      auto Load = IRB.CreateLoad(Int1Ty, FlagPtr);
-      auto ThenTerm =
-          SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false);
-      IRBuilder<> ThenIRB(ThenTerm);
-      auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr);
-      SetNoSanitizeMetadata(Load);
-      SetNoSanitizeMetadata(Store);
-
-    }
-
-  */
-
-  if (Options.StackDepth && IsEntryBB && !IsLeafFunc) {
-
-    // Check stack depth.  If it's the deepest so far, record it.
-    Module   *M = F.getParent();
-    Function *GetFrameAddr = Intrinsic::getDeclaration(
-        M, Intrinsic::frameaddress,
-        IRB.getInt8PtrTy(M->getDataLayout().getAllocaAddrSpace()));
-    auto FrameAddrPtr =
-        IRB.CreateCall(GetFrameAddr, {Constant::getNullValue(Int32Ty)});
-    auto        FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy);
-    auto        LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack);
-    auto        IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack);
-    auto        ThenTerm = SplitBlockAndInsertIfThen(IsStackLower, &*IP, false);
-    IRBuilder<> ThenIRB(ThenTerm);
-    auto        Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack);
-    SetNoSanitizeMetadata(LowestStack);
-    SetNoSanitizeMetadata(Store);
-
-  }
-
 }
 
 std::string ModuleSanitizerCoverageAFL::getSectionName(
diff --git a/src/afl-cc.c b/src/afl-cc.c
index 84fe70ec..9e56828c 100644
--- a/src/afl-cc.c
+++ b/src/afl-cc.c
@@ -997,7 +997,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
       if (instrument_mode == INSTRUMENT_PCGUARD) {
 
-#if LLVM_MAJOR >= 11
+#if LLVM_MAJOR >= 13
   #if defined __ANDROID__ || ANDROID
         cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
         instrument_mode = INSTRUMENT_LLVMNATIVE;
@@ -1014,7 +1014,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
         } else {
 
-    #if LLVM_MAJOR >= 11                            /* use new pass manager */
+    #if LLVM_MAJOR >= 13                            /* use new pass manager */
       #if LLVM_MAJOR < 16
           cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
       #endif
@@ -1035,12 +1035,12 @@ static void edit_params(u32 argc, char **argv, char **envp) {
   #if LLVM_MAJOR >= 4
         if (!be_quiet)
           SAYF(
-              "Using unoptimized trace-pc-guard, upgrade to llvm 10.0.1+ for "
+              "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for "
               "enhanced version.\n");
         cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
         instrument_mode = INSTRUMENT_LLVMNATIVE;
   #else
-        FATAL("pcguard instrumentation requires llvm 4.0.1+");
+        FATAL("pcguard instrumentation requires LLVM 4.0.1+");
   #endif
 #endif
 
@@ -1053,7 +1053,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
           cc_params[cc_par_cnt++] =
               "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table";
   #else
-          FATAL("pcguard instrumentation with pc-table requires llvm 6.0.1+");
+          FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+");
   #endif
 
         } else {
@@ -1063,7 +1063,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
         }
 
 #else
-        FATAL("pcguard instrumentation requires llvm 4.0.1+");
+        FATAL("pcguard instrumentation requires LLVM 4.0.1+");
 #endif
 
       } else {
@@ -2031,7 +2031,7 @@ int main(int argc, char **argv, char **envp) {
   if (!compiler_mode) {
 
     // lto is not a default because outside of afl-cc RANLIB and AR have to
-    // be set to llvm versions so this would work
+    // be set to LLVM versions so this would work
     if (have_llvm)
       compiler_mode = LLVM;
     else if (have_gcc_plugin)
@@ -2050,6 +2050,17 @@ int main(int argc, char **argv, char **envp) {
 
   }
 
+  /* if our PCGUARD implementation is not available then silently switch to
+     native LLVM PCGUARD */
+  if (compiler_mode == CLANG &&
+      (instrument_mode == INSTRUMENT_DEFAULT ||
+       instrument_mode == INSTRUMENT_PCGUARD) &&
+      find_object("SanitizerCoveragePCGUARD.so", argv[0]) == NULL) {
+
+    instrument_mode = INSTRUMENT_LLVMNATIVE;
+
+  }
+
   if (compiler_mode == GCC) {
 
     if (clang_mode) {
@@ -2096,12 +2107,12 @@ int main(int argc, char **argv, char **envp) {
         "-------------|\n"
         "MODES:                                  NCC PERSIST DICT   LAF "
         "CMPLOG SELECT\n"
-        "  [LTO] llvm LTO:          %s%s\n"
+        "  [LTO] LLVM LTO:          %s%s\n"
         "      PCGUARD              DEFAULT      yes yes     yes    yes yes "
         "   yes\n"
         "      CLASSIC                           yes yes     yes    yes yes "
         "   yes\n"
-        "  [LLVM] llvm:             %s%s\n"
+        "  [LLVM] LLVM:             %s%s\n"
         "      PCGUARD              %s      yes yes     module yes yes    "
         "yes\n"
         "      CLASSIC              %s      no  yes     module yes yes    "
@@ -2171,7 +2182,7 @@ int main(int argc, char **argv, char **envp) {
         "          (instrumentation/README.lto.md)\n"
         "  PERSIST: persistent mode support [code] (huge speed increase!)\n"
         "          (instrumentation/README.persistent_mode.md)\n"
-        "  DICT:   dictionary in the target [yes=automatic or llvm module "
+        "  DICT:   dictionary in the target [yes=automatic or LLVM module "
         "pass]\n"
         "          (instrumentation/README.lto.md + "
         "instrumentation/README.llvm.md)\n"