about summary refs log tree commit diff
path: root/instrumentation
diff options
context:
space:
mode:
Diffstat (limited to 'instrumentation')
-rw-r--r--instrumentation/README.llvm.md24
-rw-r--r--instrumentation/README.lto.md126
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc55
-rw-r--r--instrumentation/SanitizerCoveragePCGUARD.so.cc606
-rw-r--r--instrumentation/afl-compiler-rt.o.c286
-rw-r--r--instrumentation/afl-llvm-common.cc3
-rw-r--r--instrumentation/afl-llvm-common.h4
-rw-r--r--instrumentation/afl-llvm-dict2file.so.cc6
-rw-r--r--instrumentation/afl-llvm-lto-instrumentlist.so.cc2
-rw-r--r--instrumentation/afl-llvm-pass.so.cc10
-rw-r--r--instrumentation/cmplog-routines-pass.cc8
-rw-r--r--instrumentation/cmplog-switches-pass.cc4
-rw-r--r--instrumentation/compare-transform-pass.so.cc2
-rw-r--r--instrumentation/split-compares-pass.so.cc2
14 files changed, 619 insertions, 519 deletions
diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md
index c0677474..126cf1a2 100644
--- a/instrumentation/README.llvm.md
+++ b/instrumentation/README.llvm.md
@@ -280,3 +280,27 @@ Please note that the default counter implementations are not thread safe!
 
 Support for thread safe counters in mode LLVM CLASSIC can be activated with
 setting `AFL_LLVM_THREADSAFE_INST=1`.
+
+## 8) Source code coverage through instrumentation
+
+Measuring source code coverage is a common task in fuzzing, but it is very
+difficut to do in some situations (e.g. when using snapshot fuzzing).
+
+When using the `AFL_LLVM_INSTRUMENT=llvm-codecov` option, afl-cc will use
+native trace-pc-guard instrumentation but additionally select options that
+are required to utilize the instrumentation for source code coverage.
+
+In particular, it will switch the instrumentation to be per basic block
+instead of instrumenting edges, disable all guard pruning and enable the
+experimental pc-table support that allows the runtime to gather 100% of
+instrumented basic blocks at start, including their locations.
+
+Note: You must compile AFL with the `CODE_COVERAGE=1` option to enable the
+respective parts in the AFL compiler runtime. Support is currently only
+implemented for Nyx, but can in theory also work without Nyx.
+
+Note: You might have to adjust `MAP_SIZE_POW2` in include/config.h to ensure
+that your coverage map is large enough to hold all basic blocks of your
+target program without any collisions.
+
+More documentation on how to utilize this with Nyx will follow.
diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md
index a1c2f44a..df59cc2a 100644
--- a/instrumentation/README.lto.md
+++ b/instrumentation/README.lto.md
@@ -2,36 +2,37 @@
 
 ## TL;DR:
 
-This version requires a current llvm 11+ compiled from the GitHub master.
+This version requires a LLVM 11 or newer.
 
-1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better
-   coverage than anything else that is out there in the AFL world.
+1. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run
+   slightly faster and give better coverage.
 
-2. You can use it together with llvm_mode: laf-intel and the instrument file
-   listing features and can be combined with cmplog/Redqueen.
+2. You can use it together with COMPCOV, COMPLOG and the instrument file
+   listing features.
 
-3. It only works with llvm 11+.
+3. It only works with LLVM 11 or newer.
 
-4. AUTODICTIONARY feature (see below)!
+4. AUTODICTIONARY feature (see below)
 
-5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some
-   targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
+5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib AS=llvm-as`.
+   Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
 
 ## Introduction and problem description
 
-A big issue with how AFL++ works is that the basic block IDs that are set during
-compilation are random - and hence naturally the larger the number of
-instrumented locations, the higher the number of edge collisions are in the map.
-This can result in not discovering new paths and therefore degrade the
+A big issue with how vanilla AFL worked was that the basic block IDs that are
+set during compilation are random - and hence naturally the larger the number
+of instrumented locations, the higher the number of edge collisions are in the
+map. This can result in not discovering new paths and therefore degrade the
 efficiency of the fuzzing process.
 
-*This issue is underestimated in the fuzzing community!* With a 2^16 = 64kb
+*This issue is underestimated in the fuzzing community* With a 2^16 = 64kb
 standard map at already 256 instrumented blocks, there is on average one
 collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence
 the real collisions are between 750-18.000!
 
-To reach a solution that prevents any collisions took several approaches and
-many dead ends until we got to this:
+Note that PCGUARD (our own modified implementation and the SANCOV PCGUARD
+implementation from libfuzzer) also provides collision free coverage.
+It is a bit slower though and can a few targets with very early constructors.
 
 * We instrument at link time when we have all files pre-compiled.
 * To instrument at link time, we compile in LTO (link time optimization) mode.
@@ -45,9 +46,9 @@ many dead ends until we got to this:
 The result:
 
 * 10-25% speed gain compared to llvm_mode
-* guaranteed non-colliding edge coverage :-)
+* guaranteed non-colliding edge coverage 
 * The compile time, especially for binaries to an instrumented library, can be
-  much longer.
+  much (and sometimes much much) longer.
 
 Example build output from a libtiff build:
 
@@ -59,71 +60,30 @@ AUTODICTIONARY: 11 strings found
 [+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode).
 ```
 
-## Getting llvm 11+
+## Getting LLVM 11+
 
-### Installing llvm version 11 or 12
+### Installing llvm
 
-llvm 11 or even 12 should be available in all current Linux repositories. If you
-use an outdated Linux distribution, read the next section.
-
-### Installing llvm from the llvm repository (version 12+)
-
-Installing the llvm snapshot builds is easy and mostly painless:
-
-In the following line, change `NAME` for your Debian or Ubuntu release name
-(e.g., buster, focal, eon, etc.):
+The best way to install LLVM is to follow [https://apt.llvm.org/](https://apt.llvm.org/)
 
+e.g. for LLVM 15:
 ```
-echo deb http://apt.llvm.org/NAME/ llvm-toolchain-NAME NAME >> /etc/apt/sources.list
+wget https://apt.llvm.org/llvm.sh
+chmod +x llvm.sh
+sudo ./llvm.sh 15 all
 ```
 
-Then add the pgp key of llvm and install the packages:
+LLVM 11 to 16 should be available in all current Linux repositories.
 
-```
-wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
-apt-get update && apt-get upgrade -y
-apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \
-    libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \
-    libclang-common-12-dev libclang-cpp12 libclang-cpp12-dev liblld-12 \
-    liblld-12-dev liblldb-12 liblldb-12-dev libllvm12 libomp-12-dev \
-    libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools
-```
+## How to build afl-clang-lto
+
+That part is easy.
+Just set `LLVM_CONFIG` to the llvm-config-VERSION and build AFL++, e.g. for
+LLVM 15:
 
-### Building llvm yourself (version 12+)
-
-Building llvm from GitHub takes quite some time and is not painless:
-
-```sh
-sudo apt install binutils-dev  # this is *essential*!
-git clone --depth=1 https://github.com/llvm/llvm-project
-cd llvm-project
-mkdir build
-cd build
-
-# Add -G Ninja if ninja-build installed
-# "Building with ninja significantly improves your build time, especially with
-# incremental builds, and improves your memory usage."
-cmake \
-    -DCLANG_INCLUDE_DOCS="OFF" \
-    -DCMAKE_BUILD_TYPE=Release \
-    -DLLVM_BINUTILS_INCDIR=/usr/include/ \
-    -DLLVM_BUILD_LLVM_DYLIB="ON" \
-    -DLLVM_ENABLE_BINDINGS="OFF" \
-    -DLLVM_ENABLE_PROJECTS='clang;compiler-rt;libcxx;libcxxabi;libunwind;lld' \
-    -DLLVM_ENABLE_WARNINGS="OFF" \
-    -DLLVM_INCLUDE_BENCHMARKS="OFF" \
-    -DLLVM_INCLUDE_DOCS="OFF" \
-    -DLLVM_INCLUDE_EXAMPLES="OFF" \
-    -DLLVM_INCLUDE_TESTS="OFF" \
-    -DLLVM_LINK_LLVM_DYLIB="ON" \
-    -DLLVM_TARGETS_TO_BUILD="host" \
-    ../llvm/
-# NOTE: for llvm 16 this needs to be changed to:
-#    -DLLVM_ENABLE_PROJECTS='clang;compiler-rt;lld' \
-#    -DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi' \
-cmake --build . -j4
-export LLVM_CONFIG="$(pwd)/bin/llvm-config"
-cd /path/to/AFLplusplus/
+```
+cd ~/AFLplusplus
+export LLVM_CONFIG=llvm-config-15
 make
 sudo make install
 ```
@@ -136,10 +96,10 @@ Also, the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST ->
 [README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov
 (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
 
-Example:
+Example (note that you might need to add the version, e.g. `llvm-ar-15`:
 
 ```
-CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure
+CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar AS=llvm-as ./configure
 make
 ```
 
@@ -317,13 +277,13 @@ AS=llvm-as  ...
 afl-clang-lto is still work in progress.
 
 Known issues:
-* Anything that llvm 11+ cannot compile, afl-clang-lto cannot compile either -
+* Anything that LLVM 11+ cannot compile, afl-clang-lto cannot compile either -
   obviously.
 * Anything that does not compile with LTO, afl-clang-lto cannot compile either -
   obviously.
 
 Hence, if building a target with afl-clang-lto fails, try to build it with
-llvm12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`,
+LLVM 12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`,
 and `CXXFLAGS=-flto=full`).
 
 If this succeeds, then there is an issue with afl-clang-lto. Please report at
@@ -341,7 +301,7 @@ knows what this is doing. And the developer who implemented this didn't respond
 to emails.)
 
 In December then came the idea to implement this as a pass that is run via the
-llvm "opt" program, which is performed via an own linker that afterwards calls
+LLVM "opt" program, which is performed via an own linker that afterwards calls
 the real linker. This was first implemented in January and work ... kinda. The
 LTO time instrumentation worked, however, "how" the basic blocks were
 instrumented was a problem, as reducing duplicates turned out to be very, very
@@ -353,13 +313,13 @@ dead-end too.
 The final idea to solve this came from domenukk who proposed to insert a block
 into an edge and then just use incremental counters ... and this worked! After
 some trials and errors to implement this vanhauser-thc found out that there is
-actually an llvm function for this: SplitEdge() :-)
+actually an LLVM function for this: SplitEdge() :-)
 
-Still more problems came up though as this only works without bugs from llvm 9
+Still more problems came up though as this only works without bugs from LLVM 9
 onwards, and with high optimization the link optimization ruins the instrumented
 control flow graph.
 
-This is all now fixed with llvm 11+. The llvm's own linker is now able to load
+This is all now fixed with LLVM 11+. The llvm's own linker is now able to load
 passes and this bypasses all problems we had.
 
 Happy end :)
diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc
index 85f13c19..2d17ffd4 100644
--- a/instrumentation/SanitizerCoverageLTO.so.cc
+++ b/instrumentation/SanitizerCoverageLTO.so.cc
@@ -1,4 +1,4 @@
-/* SanitizeCoverage.cpp ported to afl++ LTO :-) */
+/* SanitizeCoverage.cpp ported to AFL++ LTO :-) */
 
 #define AFL_LLVM_PASS
 
@@ -17,8 +17,12 @@
 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Analysis/EHPersonalities.h"
+#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/Analysis/ValueTracking.h"
 #include "llvm/IR/BasicBlock.h"
@@ -47,7 +51,9 @@
 #include "llvm/Support/VirtualFileSystem.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Instrumentation.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Transforms/Utils/ModuleUtils.h"
@@ -232,7 +238,7 @@ class ModuleSanitizerCoverageLTO
 
   SanitizerCoverageOptions Options;
 
-  // afl++ START
+  // AFL++ START
   // const SpecialCaseList *          Allowlist;
   // const SpecialCaseList *          Blocklist;
   uint32_t                         autodictionary = 1;
@@ -258,7 +264,7 @@ class ModuleSanitizerCoverageLTO
   Value                           *MapPtrFixed = NULL;
   std::ofstream                    dFile;
   size_t                           found = 0;
-  // afl++ END
+  // AFL++ END
 
 };
 
@@ -325,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(
@@ -402,7 +408,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
   Int8Ty = IRB.getInt8Ty();
   Int1Ty = IRB.getInt1Ty();
 
-  /* afl++ START */
+  /* AFL++ START */
   char        *ptr;
   LLVMContext &Ctx = M.getContext();
   Ct = &Ctx;
@@ -431,6 +437,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
     if ((afl_global_id = atoi(ptr)) < 0)
       FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr);
 
+  if (afl_global_id < 4) { afl_global_id = 4; }
+
   if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) {
 
     dFile.open(ptr, std::ofstream::out | std::ofstream::app);
@@ -974,7 +982,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
 
   }
 
-  // afl++ END
+  // AFL++ END
 
   SanCovTracePCIndir =
       M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy);
@@ -998,10 +1006,11 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
   for (auto &F : M)
     instrumentFunction(F, DTCallback, PDTCallback);
 
-  // afl++ START
+  // AFL++ START
   if (dFile.is_open()) dFile.close();
 
-  if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) {
+  if (!getenv("AFL_LLVM_LTO_SKIPINIT") &&
+      (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr)) {
 
     // yes we could create our own function, insert it into ctors ...
     // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o
@@ -1151,7 +1160,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
 
   }
 
-  // afl++ END
+  // AFL++ END
 
   // We don't reference these arrays directly in any of our runtime functions,
   // so we need to prevent them from being dead stripped.
@@ -1208,10 +1217,10 @@ static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
   // (catchswitch blocks).
   if (BB->getFirstInsertionPt() == BB->end()) return false;
 
-  // afl++ START
+  // AFL++ START
   if (!Options.NoPrune && &F.getEntryBlock() == BB && F.size() > 1)
     return false;
-  // afl++ END
+  // AFL++ END
 
   if (Options.NoPrune || &F.getEntryBlock() == BB) return true;
 
@@ -1253,10 +1262,10 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
   // if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName()))
   // return;
 
-  // afl++ START
+  // AFL++ START
   if (!F.size()) return;
   if (!isInInstrumentList(&F, FMNAME)) return;
-  // afl++ END
+  // AFL++ END
 
   if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
     SplitAllCriticalEdges(
@@ -1469,8 +1478,8 @@ GlobalVariable *ModuleSanitizerCoverageLTO::CreateFunctionLocalArrayInSection(
 
   ArrayType *ArrayTy = ArrayType::get(Ty, NumElements);
   auto       Array = new GlobalVariable(
-            *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage,
-            Constant::getNullValue(ArrayTy), "__sancov_gen_");
+      *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage,
+      Constant::getNullValue(ArrayTy), "__sancov_gen_");
 
 #if LLVM_VERSION_MAJOR >= 13
   if (TargetTriple.supportsCOMDAT() &&
@@ -1554,7 +1563,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
 
   for (size_t i = 0, N = AllBlocks.size(); i < N; i++) {
 
-    // afl++ START
+    // AFL++ START
     if (BlockList.size()) {
 
       int skip = 0;
@@ -1576,7 +1585,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
 
     }
 
-    // afl++ END
+    // AFL++ END
 
     InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc);
 
@@ -1642,7 +1651,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function   &F,
 
   if (Options.TracePCGuard) {
 
-    // afl++ START
+    // AFL++ START
     ++afl_global_id;
 
     if (dFile.is_open()) {
@@ -1706,7 +1715,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function   &F,
     // done :)
 
     inst++;
-    // afl++ END
+    // AFL++ END
 
     /*
     XXXXXXXXXXXXXXXXXXX
@@ -1779,6 +1788,7 @@ INITIALIZE_PASS_END(ModuleSanitizerCoverageLTOLegacyPass, "sancov-lto",
                     "Pass for instrumenting coverage on functions", false,
                     false)
 
+#if LLVM_VERSION_MAJOR < 16
 static void registerLTOPass(const PassManagerBuilder &,
                             legacy::PassManagerBase &PM) {
 
@@ -1787,7 +1797,6 @@ static void registerLTOPass(const PassManagerBuilder &,
 
 }
 
-#if LLVM_VERSION_MAJOR < 16
 static RegisterStandardPasses RegisterCompTransPass(
     PassManagerBuilder::EP_OptimizerLast, registerLTOPass);
 
diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc
index c4a564f7..7171e7aa 100644
--- a/instrumentation/SanitizerCoveragePCGUARD.so.cc
+++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc
@@ -13,38 +13,63 @@
 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Analysis/EHPersonalities.h"
+#if LLVM_VERSION_MAJOR >= 15
+  #if LLVM_VERSION_MAJOR < 17
+    #include "llvm/ADT/Triple.h"
+  #endif
+#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/IR/EHPersonalities.h"
+#else
+  #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"
@@ -54,7 +79,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";
@@ -64,22 +90,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";
@@ -95,27 +112,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;
 
 }
@@ -135,20 +134,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,
@@ -169,20 +161,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,
@@ -211,18 +204,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) {
 
@@ -234,8 +225,7 @@ llvmGetPassPluginInfo() {
 
 }
 
-#endif
-
+#if LLVM_VERSION_MAJOR == 1
 PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module                &M,
                                                   ModuleAnalysisManager &MAM) {
 
@@ -253,34 +243,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())
@@ -291,7 +312,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);
 
 }
 
@@ -303,8 +325,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()) {
@@ -328,7 +351,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);
 
   }
 
@@ -340,37 +362,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;
@@ -393,16 +403,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;
@@ -432,26 +440,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");
@@ -461,8 +456,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 =
@@ -477,40 +470,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 && Options.PCTable) {
+  if (Ctor && debug) {
 
-    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" : "",
@@ -531,39 +509,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
@@ -578,10 +553,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) &&
@@ -593,38 +564,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_"))
@@ -643,15 +623,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);
@@ -661,47 +639,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);
 
 }
 
@@ -710,36 +669,33 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection(
 
   ArrayType *ArrayTy = ArrayType::get(Ty, NumElements);
   auto       Array = new GlobalVariable(
-            *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage,
-            Constant::getNullValue(ArrayTy), "__sancov_gen_");
+      *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;
 
@@ -764,8 +720,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
 
     }
 
@@ -788,21 +748,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;
 
@@ -851,7 +803,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
 
         }
 
-#if (LLVM_VERSION_MAJOR >= 12)
         else if (t->getTypeID() == llvm::Type::FixedVectorTyID) {
 
           FixedVectorType *tt = dyn_cast<FixedVectorType>(t);
@@ -864,16 +815,14 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
 
         }
 
-#endif
-
       }
 
     }
 
   }
 
-  /* Create PCGUARD array */
   CreateFunctionLocalArrays(F, AllBlocks, first + cnt_cov + cnt_sel_inc);
+
   if (first) { first = 0; }
   selects += cnt_sel;
 
@@ -885,12 +834,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();
@@ -1029,12 +972,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);
 
             }
@@ -1059,13 +996,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 */
@@ -1155,29 +1085,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, ... })
@@ -1233,41 +1140,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) {
 
@@ -1317,27 +1189,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(
@@ -1395,57 +1284,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/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c
index a88396d4..3f8b519b 100644
--- a/instrumentation/afl-compiler-rt.o.c
+++ b/instrumentation/afl-compiler-rt.o.c
@@ -14,6 +14,16 @@
 
 */
 
+#ifdef __AFL_CODE_COVERAGE
+  #ifndef _GNU_SOURCE
+    #define _GNU_SOURCE
+  #endif
+  #ifndef __USE_GNU
+    #define __USE_GNU
+  #endif
+  #include <dlfcn.h>
+#endif
+
 #ifdef __ANDROID__
   #include "android-ashmem.h"
 #endif
@@ -105,6 +115,44 @@ u32 __afl_dictionary_len;
 u64 __afl_map_addr;
 u32 __afl_first_final_loc;
 
+#ifdef __AFL_CODE_COVERAGE
+typedef struct afl_module_info_t afl_module_info_t;
+
+struct afl_module_info_t {
+
+  // A unique id starting with 0
+  u32 id;
+
+  // Name and base address of the module
+  char     *name;
+  uintptr_t base_address;
+
+  // PC Guard start/stop
+  u32 start;
+  u32 stop;
+
+  // PC Table begin/end
+  const uintptr_t *pcs_beg;
+  const uintptr_t *pcs_end;
+
+  u8 mapped;
+
+  afl_module_info_t *next;
+
+};
+
+typedef struct {
+
+  uintptr_t PC, PCFlags;
+
+} PCTableEntry;
+
+afl_module_info_t *__afl_module_info = NULL;
+
+u32        __afl_pcmap_size = 0;
+uintptr_t *__afl_pcmap_ptr = NULL;
+#endif  // __AFL_CODE_COVERAGE
+
 /* 1 if we are running in afl, and the forkserver was started, else 0 */
 u32 __afl_connected = 0;
 
@@ -113,7 +161,7 @@ int        __afl_selective_coverage __attribute__((weak));
 int        __afl_selective_coverage_start_off __attribute__((weak));
 static int __afl_selective_coverage_temp = 1;
 
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
 PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX];
 PREV_LOC_T __afl_prev_caller[CTX_MAX_K];
 u32        __afl_prev_ctx;
@@ -496,11 +544,12 @@ static void __afl_map_shm(void) {
 
     if (__afl_map_size && __afl_map_size > MAP_SIZE) {
 
-             u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE");
-             if (!map_env || atoi((char *)map_env) < MAP_SIZE) {
+      u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE");
+      if (!map_env || atoi((char *)map_env) < MAP_SIZE) {
 
-               send_forkserver_error(FS_ERROR_MAP_SIZE);
-               _exit(1);
+        fprintf(stderr, "FS_ERROR_MAP_SIZE\n");
+        send_forkserver_error(FS_ERROR_MAP_SIZE);
+        _exit(1);
 
       }
 
@@ -512,13 +561,13 @@ static void __afl_map_shm(void) {
 
     if (!__afl_area_ptr || __afl_area_ptr == (void *)-1) {
 
-             if (__afl_map_addr)
+      if (__afl_map_addr)
         send_forkserver_error(FS_ERROR_MAP_ADDR);
       else
         send_forkserver_error(FS_ERROR_SHMAT);
 
       perror("shmat for map");
-             _exit(1);
+      _exit(1);
 
     }
 
@@ -678,6 +727,27 @@ static void __afl_map_shm(void) {
 
   }
 
+#ifdef __AFL_CODE_COVERAGE
+  char *pcmap_id_str = getenv("__AFL_PCMAP_SHM_ID");
+
+  if (pcmap_id_str) {
+
+    __afl_pcmap_size = __afl_map_size * sizeof(void *);
+    u32 shm_id = atoi(pcmap_id_str);
+
+    __afl_pcmap_ptr = (uintptr_t *)shmat(shm_id, NULL, 0);
+
+    if (__afl_debug) {
+
+      fprintf(stderr, "DEBUG: Received %p via shmat for pcmap\n",
+              __afl_pcmap_ptr);
+
+    }
+
+  }
+
+#endif  // __AFL_CODE_COVERAGE
+
 }
 
 /* unmap SHM. */
@@ -686,6 +756,17 @@ static void __afl_unmap_shm(void) {
 
   if (!__afl_already_initialized_shm) return;
 
+#ifdef __AFL_CODE_COVERAGE
+  if (__afl_pcmap_size) {
+
+    shmdt((void *)__afl_pcmap_ptr);
+    __afl_pcmap_ptr = NULL;
+    __afl_pcmap_size = 0;
+
+  }
+
+#endif  // __AFL_CODE_COVERAGE
+
   char *id_str = getenv(SHM_ENV_VAR);
 
   if (id_str) {
@@ -1507,6 +1588,102 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
 
 }
 
+#ifdef __AFL_CODE_COVERAGE
+void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
+                              const uintptr_t *pcs_end) {
+
+  if (__afl_debug) {
+
+    fprintf(stderr, "DEBUG: __sanitizer_cov_pcs_init called\n");
+
+  }
+
+  // If for whatever reason, we cannot get dlinfo here, then pc_guard_init also
+  // couldn't get it and we'd end up attributing to the wrong module.
+  Dl_info dlinfo;
+  if (!dladdr(__builtin_return_address(0), &dlinfo)) {
+
+    fprintf(stderr,
+            "WARNING: Ignoring __sanitizer_cov_pcs_init callback due to "
+            "missing module info\n");
+    return;
+
+  }
+
+  afl_module_info_t *last_module_info = __afl_module_info;
+  while (last_module_info && last_module_info->next) {
+
+    last_module_info = last_module_info->next;
+
+  }
+
+  if (!last_module_info) {
+
+    fprintf(stderr,
+            "ERROR: __sanitizer_cov_pcs_init called with no module info?!\n");
+    abort();
+
+  }
+
+  last_module_info->pcs_beg = pcs_beg;
+  last_module_info->pcs_end = pcs_end;
+
+  // Now update the pcmap. If this is the last module coming in, after all
+  // pre-loaded code, then this will also map all of our delayed previous
+  // modules.
+
+  if (!__afl_pcmap_ptr) { return; }
+
+  for (afl_module_info_t *mod_info = __afl_module_info; mod_info;
+       mod_info = mod_info->next) {
+
+    if (mod_info->mapped) { continue; }
+
+    PCTableEntry *start = (PCTableEntry *)(mod_info->pcs_beg);
+    PCTableEntry *end = (PCTableEntry *)(mod_info->pcs_end);
+
+    u32 in_module_index = 0;
+
+    while (start < end) {
+
+      if (mod_info->start + in_module_index >= __afl_map_size) {
+
+        fprintf(stderr, "ERROR: __sanitizer_cov_pcs_init out of bounds?!\n");
+        abort();
+
+      }
+
+      uintptr_t PC = start->PC;
+
+      // This is what `GetPreviousInstructionPc` in sanitizer runtime does
+      // for x86/x86-64. Needs more work for ARM and other archs.
+      PC = PC - 1;
+
+      // Calculate relative offset in module
+      PC = PC - mod_info->base_address;
+
+      __afl_pcmap_ptr[mod_info->start + in_module_index] = PC;
+
+      start++;
+      in_module_index++;
+
+    }
+
+    mod_info->mapped = 1;
+
+    if (__afl_debug) {
+
+      fprintf(stderr, "DEBUG: __sanitizer_cov_pcs_init initialized %u PCs\n",
+              in_module_index);
+
+    }
+
+  }
+
+}
+
+#endif  // __AFL_CODE_COVERAGE
+
 /* Init callback. Populates instrumentation IDs. Note that we're using
    ID of 0 as a special value to indicate non-instrumented bits. That may
    still touch the bitmap, but in a fairly harmless way. */
@@ -1536,7 +1713,63 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
 
   }
 
-  if (start == stop || *start) return;
+  if (start == stop || *start) { return; }
+
+#ifdef __AFL_CODE_COVERAGE
+  u32               *orig_start = start;
+  afl_module_info_t *mod_info = NULL;
+
+  Dl_info dlinfo;
+  if (dladdr(__builtin_return_address(0), &dlinfo)) {
+
+    if (__afl_already_initialized_forkserver) {
+
+      fprintf(stderr, "[pcmap] Error: Module was not preloaded: %s\n",
+              dlinfo.dli_fname);
+
+    } else {
+
+      afl_module_info_t *last_module_info = __afl_module_info;
+      while (last_module_info && last_module_info->next) {
+
+        last_module_info = last_module_info->next;
+
+      }
+
+      mod_info = malloc(sizeof(afl_module_info_t));
+
+      mod_info->id = last_module_info ? last_module_info->id + 1 : 0;
+      mod_info->name = strdup(dlinfo.dli_fname);
+      mod_info->base_address = (uintptr_t)dlinfo.dli_fbase;
+      mod_info->start = 0;
+      mod_info->stop = 0;
+      mod_info->pcs_beg = NULL;
+      mod_info->pcs_end = NULL;
+      mod_info->mapped = 0;
+      mod_info->next = NULL;
+
+      if (last_module_info) {
+
+        last_module_info->next = mod_info;
+
+      } else {
+
+        __afl_module_info = mod_info;
+
+      }
+
+      fprintf(stderr, "[pcmap] Module: %s Base Address: %p\n", dlinfo.dli_fname,
+              dlinfo.dli_fbase);
+
+    }
+
+  } else {
+
+    fprintf(stderr, "[pcmap] dladdr call failed\n");
+
+  }
+
+#endif  // __AFL_CODE_COVERAGE
 
   x = getenv("AFL_INST_RATIO");
   if (x) {
@@ -1563,16 +1796,27 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
           "[-] FATAL: forkserver is already up, but an instrumented dlopen() "
           "library loaded afterwards. You must AFL_PRELOAD such libraries to "
           "be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.\n"
-          "To ignore this set AFL_IGNORE_PROBLEMS=1.\n");
+          "To ignore this set AFL_IGNORE_PROBLEMS=1 but this will lead to "
+          "ambiguous coverage data.\n"
+          "In addition, you can set AFL_IGNORE_PROBLEMS_COVERAGE=1 to "
+          "ignore the additional coverage instead (use with caution!).\n");
       abort();
 
     } else {
 
-      static u32 offset = 4;
+      u8 ignore_dso_after_fs = !!getenv("AFL_IGNORE_PROBLEMS_COVERAGE");
+      if (__afl_debug && ignore_dso_after_fs) {
+
+        fprintf(stderr, "Ignoring coverage from dynamically loaded code\n");
+
+      }
+
+      static u32 offset = 5;
 
       while (start < stop) {
 
-        if (likely(inst_ratio == 100) || R(100) < inst_ratio) {
+        if (!ignore_dso_after_fs &&
+            (likely(inst_ratio == 100) || R(100) < inst_ratio)) {
 
           *(start++) = offset;
 
@@ -1582,7 +1826,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
 
         }
 
-        if (unlikely(++offset >= __afl_final_loc)) { offset = 4; }
+        if (unlikely(++offset >= __afl_final_loc)) { offset = 5; }
 
       }
 
@@ -1596,7 +1840,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
      to avoid duplicate calls (which can happen as an artifact of the underlying
      implementation in LLVM). */
 
-  if (__afl_final_loc < 3) __afl_final_loc = 3;  // we skip the first 4 entries
+  if (__afl_final_loc < 5) __afl_final_loc = 5;  // we skip the first 5 entries
 
   *(start++) = ++__afl_final_loc;
 
@@ -1614,6 +1858,22 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
 
   }
 
+#ifdef __AFL_CODE_COVERAGE
+  if (mod_info) {
+
+    mod_info->start = *orig_start;
+    mod_info->stop = *(stop - 1);
+    if (__afl_debug) {
+
+      fprintf(stderr, "DEBUG: [pcmap] Start Index: %u Stop Index: %u\n",
+              mod_info->start, mod_info->stop);
+
+    }
+
+  }
+
+#endif  // __AFL_CODE_COVERAGE
+
   if (__afl_debug) {
 
     fprintf(stderr,
diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc
index dc34d191..7f17b02d 100644
--- a/instrumentation/afl-llvm-common.cc
+++ b/instrumentation/afl-llvm-common.cc
@@ -289,6 +289,7 @@ void scanForDangerousFunctions(llvm::Module *M) {
 
     StringRef ifunc_name = IF.getName();
     Constant *r = IF.getResolver();
+    if (r->getNumOperands() == 0) { continue; }
     StringRef r_name = cast<Function>(r->getOperand(0))->getName();
     if (!be_quiet)
       fprintf(stderr,
@@ -583,7 +584,7 @@ bool isInInstrumentList(llvm::Function *F, std::string Filename) {
 }
 
 // Calculate the number of average collisions that would occur if all
-// location IDs would be assigned randomly (like normal afl/afl++).
+// location IDs would be assigned randomly (like normal afl/AFL++).
 // This uses the "balls in bins" algorithm.
 unsigned long long int calculateCollisions(uint32_t edges) {
 
diff --git a/instrumentation/afl-llvm-common.h b/instrumentation/afl-llvm-common.h
index 16a13da5..23f67179 100644
--- a/instrumentation/afl-llvm-common.h
+++ b/instrumentation/afl-llvm-common.h
@@ -22,7 +22,9 @@ typedef long double max_align_t;
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/MathExtras.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 
 #if LLVM_VERSION_MAJOR > 3 || \
     (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc
index 97f1d47f..8ee13010 100644
--- a/instrumentation/afl-llvm-dict2file.so.cc
+++ b/instrumentation/afl-llvm-dict2file.so.cc
@@ -53,7 +53,9 @@
 #include "llvm/IR/Verifier.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/ValueTracking.h"
@@ -744,7 +746,7 @@ static void registerAFLdict2filePass(const PassManagerBuilder &,
 }
 
 static RegisterPass<AFLdict2filePass> X("afl-dict2file",
-                                        "afl++ dict2file instrumentation pass",
+                                        "AFL++ dict2file instrumentation pass",
                                         false, false);
 
 static RegisterStandardPasses RegisterAFLdict2filePass(
diff --git a/instrumentation/afl-llvm-lto-instrumentlist.so.cc b/instrumentation/afl-llvm-lto-instrumentlist.so.cc
index db5bd55e..61f97d77 100644
--- a/instrumentation/afl-llvm-lto-instrumentlist.so.cc
+++ b/instrumentation/afl-llvm-lto-instrumentlist.so.cc
@@ -45,7 +45,7 @@
 #include "llvm/IR/Module.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Debug.h"
-//#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+// #include "llvm/Transforms/IPO/PassManagerBuilder.h"
 #include "llvm/Passes/PassPlugin.h"
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/IR/PassManager.h"
diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc
index e8d0b1e5..c59324fd 100644
--- a/instrumentation/afl-llvm-pass.so.cc
+++ b/instrumentation/afl-llvm-pass.so.cc
@@ -413,7 +413,7 @@ bool AFLCoverage::runOnModule(Module &M) {
   GlobalVariable *AFLContext = NULL;
 
   if (ctx_str || caller_str)
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
     AFLContext = new GlobalVariable(
         M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
 #else
@@ -424,7 +424,7 @@ bool AFLCoverage::runOnModule(Module &M) {
 
 #ifdef AFL_HAVE_VECTOR_INTRINSICS
   if (ngram_size)
-  #if defined(__ANDROID__) || defined(__HAIKU__)
+  #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
     AFLPrevLoc = new GlobalVariable(
         M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
         /* Initializer */ nullptr, "__afl_prev_loc");
@@ -437,7 +437,7 @@ bool AFLCoverage::runOnModule(Module &M) {
   #endif
   else
 #endif
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
     AFLPrevLoc = new GlobalVariable(
         M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
 #else
@@ -448,7 +448,7 @@ bool AFLCoverage::runOnModule(Module &M) {
 
 #ifdef AFL_HAVE_VECTOR_INTRINSICS
   if (ctx_k)
-  #if defined(__ANDROID__) || defined(__HAIKU__)
+  #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
     AFLPrevCaller = new GlobalVariable(
         M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
         /* Initializer */ nullptr, "__afl_prev_caller");
@@ -461,7 +461,7 @@ bool AFLCoverage::runOnModule(Module &M) {
   #endif
   else
 #endif
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
     AFLPrevCaller =
         new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
                            "__afl_prev_caller");
diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc
index 0498156d..c3fbed8d 100644
--- a/instrumentation/cmplog-routines-pass.cc
+++ b/instrumentation/cmplog-routines-pass.cc
@@ -38,7 +38,9 @@
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Pass.h"
 #include "llvm/Analysis/ValueTracking.h"
@@ -540,7 +542,7 @@ bool CmpLogRoutines::hookRtns(Module &M) {
     Value               *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
     Value               *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
     Value               *v3Pbitcast = IRB.CreateBitCast(
-                      v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
+        v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
     Value *v3Pcasted =
         IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false);
     args.push_back(v1Pcasted);
@@ -606,7 +608,7 @@ bool CmpLogRoutines::hookRtns(Module &M) {
     Value               *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
     Value               *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
     Value               *v3Pbitcast = IRB.CreateBitCast(
-                      v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
+        v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
     Value *v3Pcasted =
         IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false);
     args.push_back(v1Pcasted);
diff --git a/instrumentation/cmplog-switches-pass.cc b/instrumentation/cmplog-switches-pass.cc
index cd0ae76d..38de669d 100644
--- a/instrumentation/cmplog-switches-pass.cc
+++ b/instrumentation/cmplog-switches-pass.cc
@@ -39,7 +39,9 @@
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Pass.h"
 #include "llvm/Analysis/ValueTracking.h"
diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc
index efc99d20..5dd705cf 100644
--- a/instrumentation/compare-transform-pass.so.cc
+++ b/instrumentation/compare-transform-pass.so.cc
@@ -623,7 +623,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
 
         IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt()));
         Value      *icmp = cur_lenchk_IRB.CreateICmpEQ(
-                 sizedValue, ConstantInt::get(sizedValue->getType(), i));
+            sizedValue, ConstantInt::get(sizedValue->getType(), i));
         cur_lenchk_IRB.CreateCondBr(icmp, end_bb, cur_cmp_bb);
         cur_lenchk_bb->getTerminator()->eraseFromParent();
 
diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc
index 8a07610c..aec6758e 100644
--- a/instrumentation/split-compares-pass.so.cc
+++ b/instrumentation/split-compares-pass.so.cc
@@ -60,7 +60,7 @@ using namespace llvm;
 
 // uncomment this toggle function verification at each step. horribly slow, but
 // helps to pinpoint a potential problem in the splitting code.
-//#define VERIFY_TOO_MUCH 1
+// #define VERIFY_TOO_MUCH 1
 
 namespace {