about summary refs log tree commit diff
path: root/src/afl-cc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/afl-cc.c')
-rw-r--r--src/afl-cc.c644
1 files changed, 429 insertions, 215 deletions
diff --git a/src/afl-cc.c b/src/afl-cc.c
index 7b059d40..9e56828c 100644
--- a/src/afl-cc.c
+++ b/src/afl-cc.c
@@ -31,6 +31,8 @@
 #include <strings.h>
 #include <limits.h>
 #include <assert.h>
+#include <ctype.h>
+#include <sys/stat.h>
 
 #if (LLVM_MAJOR - 0 == 0)
   #undef LLVM_MAJOR
@@ -76,6 +78,7 @@ enum {
   INSTRUMENT_OPT_NGRAM = 16,
   INSTRUMENT_OPT_CALLER = 32,
   INSTRUMENT_OPT_CTX_K = 64,
+  INSTRUMENT_OPT_CODECOV = 128,
 
 };
 
@@ -375,15 +378,308 @@ void parse_fsanitize(char *string) {
 
 }
 
+static u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0,
+          shared_linking = 0, preprocessor_only = 0, have_unroll = 0,
+          have_o = 0, have_pic = 0, have_c = 0, partial_linking = 0,
+          non_dash = 0;
+
+static void process_params(u32 argc, char **argv) {
+
+  if (cc_par_cnt + argc >= 1024) { FATAL("Too many command line parameters"); }
+
+  if (lto_mode && argc > 1) {
+
+    u32 idx;
+    for (idx = 1; idx < argc; idx++) {
+
+      if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1;
+
+    }
+
+  }
+
+  // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]);
+
+  /* Process the argument list. */
+
+  u8 skip_next = 0;
+  while (--argc) {
+
+    u8 *cur = *(++argv);
+
+    if (skip_next) {
+
+      skip_next = 0;
+      continue;
+
+    }
+
+    if (cur[0] != '-') { non_dash = 1; }
+    if (!strncmp(cur, "--afl", 5)) continue;
+
+    if (lto_mode && !strncmp(cur, "-flto=thin", 10)) {
+
+      FATAL(
+          "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or "
+          "use afl-clang-fast!");
+
+    }
+
+    if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue;
+    if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue;
+    if (!strncmp(cur, "-fno-unroll", 11)) continue;
+    if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue;
+    if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") ||
+        !strcmp(cur, "--no-undefined")) {
+
+      continue;
+
+    }
+
+    if (compiler_mode == GCC_PLUGIN && !strcmp(cur, "-pipe")) { continue; }
+
+    if (!strcmp(cur, "-z") || !strcmp(cur, "-Wl,-z")) {
+
+      u8 *param = *(argv + 1);
+      if (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs")) {
+
+        skip_next = 1;
+        continue;
+
+      }
+
+    }
+
+    if ((compiler_mode == GCC || compiler_mode == GCC_PLUGIN) &&
+        !strncmp(cur, "-stdlib=", 8)) {
+
+      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
+      continue;
+
+    }
+
+    if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) {
+
+      have_instr_list = 1;
+
+    }
+
+    if (!strncmp(cur, "-fsanitize=", strlen("-fsanitize=")) &&
+        strchr(cur, ',')) {
+
+      parse_fsanitize(cur);
+      if (!cur || strlen(cur) <= strlen("-fsanitize=")) { continue; }
+
+    } else if ((!strncmp(cur, "-fsanitize=fuzzer-",
+
+                         strlen("-fsanitize=fuzzer-")) ||
+                !strncmp(cur, "-fsanitize-coverage",
+                         strlen("-fsanitize-coverage"))) &&
+               (strncmp(cur, "sanitize-coverage-allow",
+                        strlen("sanitize-coverage-allow")) &&
+                strncmp(cur, "sanitize-coverage-deny",
+                        strlen("sanitize-coverage-deny")) &&
+                instrument_mode != INSTRUMENT_LLVMNATIVE)) {
+
+      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
+      continue;
+
+    }
+
+    if (need_aflpplib || !strcmp(cur, "-fsanitize=fuzzer")) {
+
+      u8 *afllib = find_object("libAFLDriver.a", argv[0]);
+
+      if (!be_quiet) {
+
+        OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
+
+      }
+
+      if (!afllib) {
+
+        if (!be_quiet) {
+
+          WARNF(
+              "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
+              "the flags - this will fail!");
+
+        }
+
+      } else {
+
+        cc_params[cc_par_cnt++] = afllib;
+
+#ifdef __APPLE__
+        cc_params[cc_par_cnt++] = "-undefined";
+        cc_params[cc_par_cnt++] = "dynamic_lookup";
+#endif
+
+      }
+
+      if (need_aflpplib) {
+
+        need_aflpplib = 0;
+
+      } else {
+
+        continue;
+
+      }
+
+    }
+
+    if (!strcmp(cur, "-m32")) bit_mode = 32;
+    if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
+    if (!strcmp(cur, "-m64")) bit_mode = 64;
+
+    if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
+      asan_set = 1;
+
+    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
+
+    if (!strcmp(cur, "-x")) x_set = 1;
+    if (!strcmp(cur, "-E")) preprocessor_only = 1;
+    if (!strcmp(cur, "-shared")) shared_linking = 1;
+    if (!strcmp(cur, "-dynamiclib")) shared_linking = 1;
+    if (!strcmp(cur, "--target=wasm32-wasi")) passthrough = 1;
+    if (!strcmp(cur, "-Wl,-r")) partial_linking = 1;
+    if (!strcmp(cur, "-Wl,-i")) partial_linking = 1;
+    if (!strcmp(cur, "-Wl,--relocatable")) partial_linking = 1;
+    if (!strcmp(cur, "-r")) partial_linking = 1;
+    if (!strcmp(cur, "--relocatable")) partial_linking = 1;
+    if (!strcmp(cur, "-c")) have_c = 1;
+
+    if (!strncmp(cur, "-O", 2)) have_o = 1;
+    if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1;
+
+    if (*cur == '@') {
+
+      // response file support.
+      // we have two choices - move everything to the command line or
+      // rewrite the response files to temporary files and delete them
+      // afterwards. We choose the first for easiness.
+      // We do *not* support quotes in the rsp files to cope with spaces in
+      // filenames etc! If you need that then send a patch!
+      u8 *filename = cur + 1;
+      if (debug) { DEBUGF("response file=%s\n", filename); }
+      FILE       *f = fopen(filename, "r");
+      struct stat st;
+
+      // Check not found or empty? let the compiler complain if so.
+      if (!f || fstat(fileno(f), &st) < 0 || st.st_size < 1) {
+
+        cc_params[cc_par_cnt++] = cur;
+        continue;
+
+      }
+
+      u8    *tmpbuf = malloc(st.st_size + 2), *ptr;
+      char **args = malloc(sizeof(char *) * (st.st_size >> 1));
+      int    count = 1, cont = 0, cont_act = 0;
+
+      while (fgets(tmpbuf, st.st_size + 1, f)) {
+
+        ptr = tmpbuf;
+        // fprintf(stderr, "1: %s\n", ptr);
+        //  no leading whitespace
+        while (isspace(*ptr)) {
+
+          ++ptr;
+          cont_act = 0;
+
+        }
+
+        // no comments, no empty lines
+        if (*ptr == '#' || *ptr == '\n' || !*ptr) { continue; }
+        // remove LF
+        if (ptr[strlen(ptr) - 1] == '\n') { ptr[strlen(ptr) - 1] = 0; }
+        // remove CR
+        if (*ptr && ptr[strlen(ptr) - 1] == '\r') { ptr[strlen(ptr) - 1] = 0; }
+        // handle \ at end of line
+        if (*ptr && ptr[strlen(ptr) - 1] == '\\') {
+
+          cont = 1;
+          ptr[strlen(ptr) - 1] = 0;
+
+        }
+
+        // fprintf(stderr, "2: %s\n", ptr);
+
+        // remove whitespace at end
+        while (*ptr && isspace(ptr[strlen(ptr) - 1])) {
+
+          ptr[strlen(ptr) - 1] = 0;
+          cont = 0;
+
+        }
+
+        // fprintf(stderr, "3: %s\n", ptr);
+        if (*ptr) {
+
+          do {
+
+            u8 *value = ptr;
+            while (*ptr && !isspace(*ptr)) {
+
+              ++ptr;
+
+            }
+
+            while (*ptr && isspace(*ptr)) {
+
+              *ptr++ = 0;
+
+            }
+
+            if (cont_act) {
+
+              u32 len = strlen(args[count - 1]) + strlen(value) + 1;
+              u8 *tmp = malloc(len);
+              snprintf(tmp, len, "%s%s", args[count - 1], value);
+              free(args[count - 1]);
+              args[count - 1] = tmp;
+              cont_act = 0;
+
+            } else {
+
+              args[count++] = strdup(value);
+
+            }
+
+          } while (*ptr);
+
+        }
+
+        if (cont) {
+
+          cont_act = 1;
+          cont = 0;
+
+        }
+
+      }
+
+      if (count) { process_params(count, args); }
+
+      // we cannot free args[]
+      free(tmpbuf);
+
+      continue;
+
+    }
+
+    cc_params[cc_par_cnt++] = cur;
+
+  }
+
+}
+
 /* Copy argv to cc_params, making the necessary edits. */
 
 static void edit_params(u32 argc, char **argv, char **envp) {
 
-  u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0,
-     preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0,
-     have_c = 0, partial_linking = 0;
-
-  cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
+  cc_params = ck_alloc(1024 * sizeof(u8 *));
 
   if (lto_mode) {
 
@@ -641,10 +937,10 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
     }
 
-    //#if LLVM_MAJOR >= 13
-    //    // Use the old pass manager in LLVM 14 which the afl++ passes still
-    //    use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager";
-    //#endif
+    // #if LLVM_MAJOR >= 13
+    //     // Use the old pass manager in LLVM 14 which the AFL++ passes still
+    //     use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager";
+    // #endif
 
     if (lto_mode && !have_c) {
 
@@ -701,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;
@@ -718,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
@@ -739,21 +1035,35 @@ 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
 
       } else if (instrument_mode == INSTRUMENT_LLVMNATIVE) {
 
 #if LLVM_MAJOR >= 4
-        cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
+        if (instrument_opt_mode & INSTRUMENT_OPT_CODECOV) {
+
+  #if LLVM_MAJOR >= 6
+          cc_params[cc_par_cnt++] =
+              "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table";
+  #else
+          FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+");
+  #endif
+
+        } else {
+
+          cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
+
+        }
+
 #else
-        FATAL("pcguard instrumentation requires llvm 4.0.1+");
+        FATAL("pcguard instrumentation requires LLVM 4.0.1+");
 #endif
 
       } else {
@@ -816,159 +1126,15 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
       }
 
-      if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC";
-
     }
 
   }
 
-  /* Detect stray -v calls from ./configure scripts. */
-
-  u8 skip_next = 0, non_dash = 0;
-  while (--argc) {
-
-    u8 *cur = *(++argv);
-
-    if (skip_next) {
-
-      skip_next = 0;
-      continue;
-
-    }
-
-    if (cur[0] != '-') { non_dash = 1; }
-    if (!strncmp(cur, "--afl", 5)) continue;
-    if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue;
-    if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue;
-    if (!strncmp(cur, "-fno-unroll", 11)) continue;
-    if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue;
-    if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") ||
-        !strcmp(cur, "--no-undefined")) {
-
-      continue;
-
-    }
-
-    if (compiler_mode == GCC_PLUGIN && !strcmp(cur, "-pipe")) { continue; }
-
-    if (!strcmp(cur, "-z") || !strcmp(cur, "-Wl,-z")) {
-
-      u8 *param = *(argv + 1);
-      if (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs")) {
-
-        skip_next = 1;
-        continue;
-
-      }
-
-    }
-
-    if ((compiler_mode == GCC || compiler_mode == GCC_PLUGIN) &&
-        !strncmp(cur, "-stdlib=", 8)) {
-
-      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
-      continue;
-
-    }
-
-    if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) {
-
-      have_instr_list = 1;
-
-    }
-
-    if (!strncmp(cur, "-fsanitize=", strlen("-fsanitize=")) &&
-        strchr(cur, ',')) {
-
-      parse_fsanitize(cur);
-      if (!cur || strlen(cur) <= strlen("-fsanitize=")) { continue; }
-
-    } else if ((!strncmp(cur, "-fsanitize=fuzzer-",
-
-                         strlen("-fsanitize=fuzzer-")) ||
-                !strncmp(cur, "-fsanitize-coverage",
-                         strlen("-fsanitize-coverage"))) &&
-               (strncmp(cur, "sanitize-coverage-allow",
-                        strlen("sanitize-coverage-allow")) &&
-                strncmp(cur, "sanitize-coverage-deny",
-                        strlen("sanitize-coverage-deny")) &&
-                instrument_mode != INSTRUMENT_LLVMNATIVE)) {
-
-      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
-      continue;
+  /* Inspect the command line parameters. */
 
-    }
-
-    if (need_aflpplib || !strcmp(cur, "-fsanitize=fuzzer")) {
+  process_params(argc, argv);
 
-      u8 *afllib = find_object("libAFLDriver.a", argv[0]);
-
-      if (!be_quiet) {
-
-        OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
-
-      }
-
-      if (!afllib) {
-
-        if (!be_quiet) {
-
-          WARNF(
-              "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
-              "the flags - this will fail!");
-
-        }
-
-      } else {
-
-        cc_params[cc_par_cnt++] = afllib;
-
-#ifdef __APPLE__
-        cc_params[cc_par_cnt++] = "-undefined";
-        cc_params[cc_par_cnt++] = "dynamic_lookup";
-#endif
-
-      }
-
-      if (need_aflpplib) {
-
-        need_aflpplib = 0;
-
-      } else {
-
-        continue;
-
-      }
-
-    }
-
-    if (!strcmp(cur, "-m32")) bit_mode = 32;
-    if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
-    if (!strcmp(cur, "-m64")) bit_mode = 64;
-
-    if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
-      asan_set = 1;
-
-    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
-
-    if (!strcmp(cur, "-x")) x_set = 1;
-    if (!strcmp(cur, "-E")) preprocessor_only = 1;
-    if (!strcmp(cur, "-shared")) shared_linking = 1;
-    if (!strcmp(cur, "-dynamiclib")) shared_linking = 1;
-    if (!strcmp(cur, "--target=wasm32-wasi")) passthrough = 1;
-    if (!strcmp(cur, "-Wl,-r")) partial_linking = 1;
-    if (!strcmp(cur, "-Wl,-i")) partial_linking = 1;
-    if (!strcmp(cur, "-Wl,--relocatable")) partial_linking = 1;
-    if (!strcmp(cur, "-r")) partial_linking = 1;
-    if (!strcmp(cur, "--relocatable")) partial_linking = 1;
-    if (!strcmp(cur, "-c")) have_c = 1;
-
-    if (!strncmp(cur, "-O", 2)) have_o = 1;
-    if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1;
-
-    cc_params[cc_par_cnt++] = cur;
-
-  }
+  if (!have_pic) { cc_params[cc_par_cnt++] = "-fPIC"; }
 
   // in case LLVM is installed not via a package manager or "make install"
   // e.g. compiled download or compiled from github then its ./lib directory
@@ -1101,37 +1267,45 @@ static void edit_params(u32 argc, char **argv, char **envp) {
   if (!have_c) cc_params[cc_par_cnt++] = "-lrt";
 #endif
 
-  cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
   cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
   cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
 
-  /* When the user tries to use persistent or deferred forkserver modes by
-     appending a single line to the program, we want to reliably inject a
-     signature into the binary (to be picked up by afl-fuzz) and we want
-     to call a function from the runtime .o file. This is unnecessarily
-     painful for three reasons:
+  /* As documented in instrumentation/README.persistent_mode.md, deferred
+     forkserver initialization and persistent mode are not available in afl-gcc
+     and afl-clang. */
+  if (compiler_mode != GCC && compiler_mode != CLANG) {
 
-     1) We need to convince the compiler not to optimize out the signature.
-        This is done with __attribute__((used)).
+    cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
 
-     2) We need to convince the linker, when called with -Wl,--gc-sections,
-        not to do the same. This is done by forcing an assignment to a
-        'volatile' pointer.
+    /* When the user tries to use persistent or deferred forkserver modes by
+       appending a single line to the program, we want to reliably inject a
+       signature into the binary (to be picked up by afl-fuzz) and we want
+       to call a function from the runtime .o file. This is unnecessarily
+       painful for three reasons:
 
-     3) We need to declare __afl_persistent_loop() in the global namespace,
-        but doing this within a method in a class is hard - :: and extern "C"
-        are forbidden and __attribute__((alias(...))) doesn't work. Hence the
-        __asm__ aliasing trick.
+       1) We need to convince the compiler not to optimize out the signature.
+          This is done with __attribute__((used)).
 
-   */
+       2) We need to convince the linker, when called with -Wl,--gc-sections,
+          not to do the same. This is done by forcing an assignment to a
+          'volatile' pointer.
 
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_FUZZ_INIT()="
-      "int __afl_sharedmem_fuzzing = 1;"
-      "extern unsigned int *__afl_fuzz_len;"
-      "extern unsigned char *__afl_fuzz_ptr;"
-      "unsigned char __afl_fuzz_alt[1048576];"
-      "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
+       3) We need to declare __afl_persistent_loop() in the global namespace,
+          but doing this within a method in a class is hard - :: and extern "C"
+          are forbidden and __attribute__((alias(...))) doesn't work. Hence the
+          __asm__ aliasing trick.
+
+     */
+
+    cc_params[cc_par_cnt++] =
+        "-D__AFL_FUZZ_INIT()="
+        "int __afl_sharedmem_fuzzing = 1;"
+        "extern unsigned int *__afl_fuzz_len;"
+        "extern unsigned char *__afl_fuzz_ptr;"
+        "unsigned char __afl_fuzz_alt[1048576];"
+        "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
+
+  }
 
   if (plusplus_mode) {
 
@@ -1169,35 +1343,39 @@ static void edit_params(u32 argc, char **argv, char **envp) {
       "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff "
       "? 0 : *__afl_fuzz_len)";
 
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_LOOP(_A)="
-      "({ static volatile char *_B __attribute__((used,unused)); "
-      " _B = (char*)\"" PERSIST_SIG
-      "\"; "
-      "extern int __afl_connected;"
+  if (compiler_mode != GCC && compiler_mode != CLANG) {
+
+    cc_params[cc_par_cnt++] =
+        "-D__AFL_LOOP(_A)="
+        "({ static volatile const char *_B __attribute__((used,unused)); "
+        " _B = (const char*)\"" PERSIST_SIG
+        "\"; "
+        "extern int __afl_connected;"
 #ifdef __APPLE__
-      "__attribute__((visibility(\"default\"))) "
-      "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
+        "__attribute__((visibility(\"default\"))) "
+        "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
 #else
-      "__attribute__((visibility(\"default\"))) "
-      "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
+        "__attribute__((visibility(\"default\"))) "
+        "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
 #endif                                                        /* ^__APPLE__ */
-      // if afl is connected, we run _A times, else once.
-      "_L(__afl_connected ? _A : 1); })";
+        // if afl is connected, we run _A times, else once.
+        "_L(__afl_connected ? _A : 1); })";
 
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_INIT()="
-      "do { static volatile char *_A __attribute__((used,unused)); "
-      " _A = (char*)\"" DEFER_SIG
-      "\"; "
+    cc_params[cc_par_cnt++] =
+        "-D__AFL_INIT()="
+        "do { static volatile const char *_A __attribute__((used,unused)); "
+        " _A = (const char*)\"" DEFER_SIG
+        "\"; "
 #ifdef __APPLE__
-      "__attribute__((visibility(\"default\"))) "
-      "void _I(void) __asm__(\"___afl_manual_init\"); "
+        "__attribute__((visibility(\"default\"))) "
+        "void _I(void) __asm__(\"___afl_manual_init\"); "
 #else
-      "__attribute__((visibility(\"default\"))) "
-      "void _I(void) __asm__(\"__afl_manual_init\"); "
+        "__attribute__((visibility(\"default\"))) "
+        "void _I(void) __asm__(\"__afl_manual_init\"); "
 #endif                                                        /* ^__APPLE__ */
-      "_I(); } while (0)";
+        "_I(); } while (0)";
+
+  }
 
   if (x_set) {
 
@@ -1639,13 +1817,17 @@ int main(int argc, char **argv, char **envp) {
           instrument_mode = INSTRUMENT_CLASSIC;
           lto_mode = 1;
 
-        } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL)
+        } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) {
 
           instrument_mode = INSTRUMENT_AFL;
-        else
+
+        } else {
+
           FATAL("main instrumentation mode already set with %s",
                 instrument_mode_string[instrument_mode]);
 
+        }
+
       }
 
       if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 ||
@@ -1660,7 +1842,8 @@ int main(int argc, char **argv, char **envp) {
       }
 
       if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 ||
-          strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0) {
+          strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 ||
+          strncasecmp(ptr2, "native", strlen("native")) == 0) {
 
         if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE)
           instrument_mode = INSTRUMENT_LLVMNATIVE;
@@ -1670,6 +1853,23 @@ int main(int argc, char **argv, char **envp) {
 
       }
 
+      if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 ||
+          strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) {
+
+        if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) {
+
+          instrument_mode = INSTRUMENT_LLVMNATIVE;
+          instrument_opt_mode |= INSTRUMENT_OPT_CODECOV;
+
+        } else {
+
+          FATAL("main instrumentation mode already set with %s",
+                instrument_mode_string[instrument_mode]);
+
+        }
+
+      }
+
       if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 ||
           strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) {
 
@@ -1831,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)
@@ -1850,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) {
@@ -1896,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    "
@@ -1971,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"
@@ -2087,6 +2298,8 @@ int main(int argc, char **argv, char **envp) {
             "bb\n"
             "  AFL_REAL_LD: use this lld linker instead of the compiled in "
             "path\n"
+            "  AFL_LLVM_LTO_SKIPINIT: don't inject initialization code "
+            "(used in WAFL mode)\n"
             "If anything fails - be sure to read README.lto.md!\n");
 #endif
 
@@ -2227,7 +2440,8 @@ int main(int argc, char **argv, char **envp) {
         "(requires LLVM 11 or higher)");
 #endif
 
-  if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC)
+  if (instrument_opt_mode && instrument_opt_mode != INSTRUMENT_OPT_CODECOV &&
+      instrument_mode != INSTRUMENT_CLASSIC)
     FATAL(
         "CALLER, CTX and NGRAM instrumentation options can only be used with "
         "the LLVM CLASSIC instrumentation mode.");