all: Use patched-in sanitizer hooks with PC instead of trampoline

We are already patching libFuzzer anyway and will continue to do so for
the foreseeable future. Thus, we might as well get rid of the highly
complex and architecture-dependent trampoline hack.
diff --git a/README.md b/README.md
index 20ee311..ff5875f 100644
--- a/README.md
+++ b/README.md
@@ -439,9 +439,6 @@
 See [ExampleValueProfileFuzzer.java](https://github.com/CodeIntelligenceTesting/jazzer/tree/main/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java)
 for a fuzz target that would be very hard to fuzz without value profile.
 
-As passing the bytecode location back to libFuzzer requires inline assembly and may thus not be fully portable, it can be disabled
-via the flag `--nofake_pcs`.
-
 ### Custom Hooks
 
 In order to obtain information about data passed into functions such as `String.equals` or `String.startsWith`, Jazzer
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java
index 5a77356..cf9b0c5 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java
+++ b/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java
@@ -28,10 +28,6 @@
     }
   }
 
-  // Making this static final ensures that the JIT will eliminate the dead branch of a construct
-  // such as:
-  // if (USE_FAKE_PCS) ... else ...
-  private static final boolean USE_FAKE_PCS = useFakePcs();
   // Note that we are not encoding as modified UTF-8 here: The FuzzedDataProvider transparently
   // converts CESU8 into modified UTF-8 by coding null bytes on two bytes. Since the fuzzer is more
   // likely to insert literal null bytes, having both the fuzzer input and the reported string
@@ -39,39 +35,6 @@
   // UTF-8.
   private static final Charset FUZZED_DATA_CHARSET = Charset.forName("CESU8");
 
-  /* trace-cmp */
-  public static void traceCmpInt(int arg1, int arg2, int pc) {
-    if (USE_FAKE_PCS) {
-      traceCmpIntWithPc(arg1, arg2, pc);
-    } else {
-      traceCmpInt(arg1, arg2);
-    }
-  }
-
-  public static void traceConstCmpInt(int arg1, int arg2, int pc) {
-    if (USE_FAKE_PCS) {
-      traceConstCmpIntWithPc(arg1, arg2, pc);
-    } else {
-      traceConstCmpInt(arg1, arg2);
-    }
-  }
-
-  public static void traceCmpLong(long arg1, long arg2, int pc) {
-    if (USE_FAKE_PCS) {
-      traceCmpLongWithPc(arg1, arg2, pc);
-    } else {
-      traceCmpLong(arg1, arg2);
-    }
-  }
-
-  public static void traceSwitch(long val, long[] cases, int pc) {
-    if (USE_FAKE_PCS) {
-      traceSwitchWithPc(val, cases, pc);
-    } else {
-      traceSwitch(val, cases);
-    }
-  }
-
   public static native void traceMemcmp(byte[] b1, byte[] b2, int result, int pc);
 
   public static void traceStrcmp(String s1, String s2, int result, int pc) {
@@ -82,48 +45,7 @@
     traceStrstr0(encodeForLibFuzzer(s2), pc);
   }
 
-  /* trace-div */
-  public static void traceDivInt(int val, int pc) {
-    if (USE_FAKE_PCS) {
-      traceDivIntWithPc(val, pc);
-    } else {
-      traceDivInt(val);
-    }
-  }
-
-  public static void traceDivLong(long val, int pc) {
-    if (USE_FAKE_PCS) {
-      traceDivLongWithPc(val, pc);
-    } else {
-      traceDivLong(val);
-    }
-  }
-
-  /* trace-gep */
-  public static void traceGep(long val, int pc) {
-    if (USE_FAKE_PCS) {
-      traceGepWithPc(val, pc);
-    } else {
-      traceGep(val);
-    }
-  }
-
-  /* indirect-calls */
-  public static void tracePcIndir(int callee, int caller) {
-    if (!USE_FAKE_PCS) {
-      // Without fake PCs, tracePcIndir will not record the relation between callee and pc, which
-      // makes it useless.
-      return;
-    }
-    tracePcIndir0(callee, caller);
-  }
-
   public static void traceReflectiveCall(Executable callee, int pc) {
-    if (!USE_FAKE_PCS) {
-      // Without fake PCs, tracePcIndir will not record the relation between callee and pc, which
-      // makes it useless.
-      return;
-    }
     String className = callee.getDeclaringClass().getCanonicalName();
     String executableName = callee.getName();
     String descriptor = Utils.getDescriptor(callee);
@@ -158,6 +80,19 @@
     }
   }
 
+  /* trace-cmp */
+  public static native void traceCmpInt(int arg1, int arg2, int pc);
+  public static native void traceConstCmpInt(int arg1, int arg2, int pc);
+  public static native void traceCmpLong(long arg1, long arg2, int pc);
+  public static native void traceSwitch(long val, long[] cases, int pc);
+  /* trace-div */
+  public static native void traceDivInt(int val, int pc);
+  public static native void traceDivLong(long val, int pc);
+  /* trace-gep */
+  public static native void traceGep(long val, int pc);
+  /* indirect-calls */
+  public static native void tracePcIndir(int callee, int caller);
+
   public static native void handleLibraryLoad();
 
   private static byte[] encodeForLibFuzzer(String str) {
@@ -166,29 +101,5 @@
     return str.substring(0, Math.min(str.length(), 64)).getBytes(FUZZED_DATA_CHARSET);
   }
 
-  private static boolean useFakePcs() {
-    String rawFakePcs = System.getProperty("jazzer.fake_pcs");
-    if (rawFakePcs == null) {
-      return false;
-    }
-    return Boolean.parseBoolean(rawFakePcs);
-  }
-
   private static native void traceStrstr0(byte[] needle, int pc);
-
-  private static native void traceCmpInt(int arg1, int arg2);
-  private static native void traceCmpIntWithPc(int arg1, int arg2, int pc);
-  private static native void traceConstCmpInt(int arg1, int arg2);
-  private static native void traceConstCmpIntWithPc(int arg1, int arg2, int pc);
-  private static native void traceCmpLong(long arg1, long arg2);
-  private static native void traceCmpLongWithPc(long arg1, long arg2, int pc);
-  private static native void traceSwitch(long val, long[] cases);
-  private static native void traceSwitchWithPc(long val, long[] cases, int pc);
-  private static native void traceDivInt(int val);
-  private static native void traceDivIntWithPc(int val, int pc);
-  private static native void traceDivLong(long val);
-  private static native void traceDivLongWithPc(long val, int pc);
-  private static native void traceGep(long val);
-  private static native void traceGepWithPc(long val, int pc);
-  private static native void tracePcIndir0(int callee, int caller);
 }
diff --git a/agent/src/main/native/com/code_intelligence/jazzer/runtime/jazzer_fuzzer_callbacks.cpp b/agent/src/main/native/com/code_intelligence/jazzer/runtime/jazzer_fuzzer_callbacks.cpp
index 6be2dae..a4b0214 100644
--- a/agent/src/main/native/com/code_intelligence/jazzer/runtime/jazzer_fuzzer_callbacks.cpp
+++ b/agent/src/main/native/com/code_intelligence/jazzer/runtime/jazzer_fuzzer_callbacks.cpp
@@ -88,72 +88,36 @@
 
 [[maybe_unused]] void
 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpLong(
-    JNIEnv *env, jclass cls, jlong value1, jlong value2) {
-  __sanitizer_cov_trace_cmp8(value1, value2);
-}
-
-extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpLong(
-    jlong value1, jlong value2) {
-  __sanitizer_cov_trace_cmp8(value1, value2);
-}
-
-[[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpLongWithPc(
     JNIEnv *env, jclass cls, jlong value1, jlong value2, jint id) {
   __sanitizer_cov_trace_cmp8_with_pc(idToPc(id), value1, value2);
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpLongWithPc(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpLong(
     jlong value1, jlong value2, jint id) {
   __sanitizer_cov_trace_cmp8_with_pc(idToPc(id), value1, value2);
 }
 
 [[maybe_unused]] void
 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpInt(
-    JNIEnv *env, jclass cls, jint value1, jint value2) {
-  __sanitizer_cov_trace_cmp4(value1, value2);
-}
-
-extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpInt(
-    jint value1, jint value2) {
-  __sanitizer_cov_trace_cmp4(value1, value2);
-}
-
-[[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpIntWithPc(
     JNIEnv *env, jclass cls, jint value1, jint value2, jint id) {
   __sanitizer_cov_trace_cmp4_with_pc(idToPc(id), value1, value2);
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpIntWithPc(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceCmpInt(
     jint value1, jint value2, jint id) {
   __sanitizer_cov_trace_cmp4_with_pc(idToPc(id), value1, value2);
 }
 
 [[maybe_unused]] void
 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceConstCmpInt(
-    JNIEnv *env, jclass cls, jint value1, jint value2) {
-  __sanitizer_cov_trace_cmp4(value1, value2);
-}
-
-extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceConstCmpInt(
-    jint value1, jint value2) {
-  __sanitizer_cov_trace_cmp4(value1, value2);
-}
-
-[[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceConstCmpIntWithPc(
     JNIEnv *env, jclass cls, jint value1, jint value2, jint id) {
   __sanitizer_cov_trace_cmp4_with_pc(idToPc(id), value1, value2);
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceConstCmpIntWithPc(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceConstCmpInt(
     jint value1, jint value2, jint id) {
   __sanitizer_cov_trace_cmp4_with_pc(idToPc(id), value1, value2);
 }
@@ -161,25 +125,6 @@
 [[maybe_unused]] void
 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceSwitch(
     JNIEnv *env, jclass cls, jlong switch_value,
-    jlongArray libfuzzer_case_values) {
-  auto *case_values = static_cast<jlong *>(
-      env->GetPrimitiveArrayCritical(libfuzzer_case_values, nullptr));
-  __sanitizer_cov_trace_switch(switch_value,
-                               reinterpret_cast<uint64_t *>(case_values));
-  env->ReleasePrimitiveArrayCritical(libfuzzer_case_values, case_values,
-                                     JNI_ABORT);
-}
-
-extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceSwitch(
-    jlong switch_value, jint libfuzzer_case_values_length, jlong *case_values) {
-  __sanitizer_cov_trace_switch(switch_value,
-                               reinterpret_cast<uint64_t *>(case_values));
-}
-
-[[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceSwitchWithPc(
-    JNIEnv *env, jclass cls, jlong switch_value,
     jlongArray libfuzzer_case_values, jint id) {
   auto *case_values = static_cast<jlong *>(
       env->GetPrimitiveArrayCritical(libfuzzer_case_values, nullptr));
@@ -190,7 +135,7 @@
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceSwitchWithPc(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceSwitch(
     jlong switch_value, jint libfuzzer_case_values_length, jlong *case_values,
     jint id) {
   __sanitizer_cov_trace_switch_with_pc(
@@ -199,85 +144,49 @@
 
 [[maybe_unused]] void
 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivLong(
-    JNIEnv *env, jclass cls, jlong value) {
-  __sanitizer_cov_trace_div8(value);
-}
-
-extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivLong(
-    jlong value) {
-  __sanitizer_cov_trace_div8(value);
-}
-
-[[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivLongWithPc(
     JNIEnv *env, jclass cls, jlong value, jint id) {
   __sanitizer_cov_trace_div8_with_pc(idToPc(id), value);
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivLongWithPc(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivLong(
     jlong value, jint id) {
   __sanitizer_cov_trace_div8_with_pc(idToPc(id), value);
 }
 
 [[maybe_unused]] void
 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivInt(
-    JNIEnv *env, jclass cls, jint value) {
-  __sanitizer_cov_trace_div4(value);
-}
-
-extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivInt(
-    jint value) {
-  __sanitizer_cov_trace_div4(value);
-}
-
-[[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivIntWithPc(
     JNIEnv *env, jclass cls, jint value, jint id) {
   __sanitizer_cov_trace_div4_with_pc(idToPc(id), value);
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivIntWithPc(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceDivInt(
     jint value, jint id) {
   __sanitizer_cov_trace_div4_with_pc(idToPc(id), value);
 }
 
 [[maybe_unused]] void
 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceGep(
-    JNIEnv *env, jclass cls, jlong idx) {
-  __sanitizer_cov_trace_gep(static_cast<uintptr_t>(idx));
-}
-
-extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceGep(
-    jlong idx) {
-  __sanitizer_cov_trace_gep(static_cast<uintptr_t>(idx));
-}
-
-[[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceGepWithPc(
     JNIEnv *env, jclass cls, jlong idx, jint id) {
   __sanitizer_cov_trace_gep_with_pc(idToPc(id), static_cast<uintptr_t>(idx));
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceGepWithPc(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_traceGep(
     jlong idx, jint id) {
   __sanitizer_cov_trace_gep_with_pc(idToPc(id), static_cast<uintptr_t>(idx));
 }
 
 [[maybe_unused]] void
-Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_tracePcIndir0(
+Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_tracePcIndir(
     JNIEnv *env, jclass cls, jint caller_id, jint callee_id) {
   __sanitizer_cov_trace_pc_indir_with_pc(idToPc(caller_id),
                                          static_cast<uintptr_t>(callee_id));
 }
 
 extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
-JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_tracePcIndir0(
+JavaCritical_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_tracePcIndir(
     jint caller_id, jint callee_id) {
   __sanitizer_cov_trace_pc_indir_with_pc(idToPc(caller_id),
                                          static_cast<uintptr_t>(callee_id));
diff --git a/driver/BUILD.bazel b/driver/BUILD.bazel
index a667b43..1d74678 100644
--- a/driver/BUILD.bazel
+++ b/driver/BUILD.bazel
@@ -3,9 +3,7 @@
 
 cc_library(
     name = "sanitizer_hooks_with_pc",
-    srcs = ["sanitizer_hooks_with_pc.cpp"],
     hdrs = ["sanitizer_hooks_with_pc.h"],
-    linkstatic = True,
     visibility = [
         "//agent/src/jmh/native/com/code_intelligence/jazzer/runtime:__pkg__",
         "//agent/src/main/native/com/code_intelligence/jazzer/runtime:__pkg__",
@@ -13,17 +11,6 @@
     ],
 )
 
-cc_test(
-    name = "sanitizer_hooks_with_pc_test",
-    size = "small",
-    srcs = ["sanitizer_hooks_with_pc_test.cpp"],
-    deps = [
-        ":sanitizer_hooks_with_pc",
-        "@googletest//:gtest",
-        "@googletest//:gtest_main",
-    ],
-)
-
 cc_library(
     name = "fuzzed_data_provider",
     srcs = [
diff --git a/driver/jazzer_main.cpp b/driver/jazzer_main.cpp
index 147852d..b96c866 100644
--- a/driver/jazzer_main.cpp
+++ b/driver/jazzer_main.cpp
@@ -42,9 +42,6 @@
 // Defined by glog
 DECLARE_bool(log_prefix);
 
-// Defined in libfuzzer_callbacks.cpp
-DECLARE_bool(fake_pcs);
-
 // Defined in jvm_tooling.cpp
 DECLARE_string(id_sync_file);
 
@@ -164,12 +161,6 @@
 
   const auto argv_end = argv + argc;
 
-  // Parse libFuzzer flags to determine Jazzer flag defaults before letting
-  // gflags parse the command line.
-  if (std::find(argv, argv_end, "-use_value_profile=1"s) != argv_end) {
-    FLAGS_fake_pcs = true;
-  }
-
   {
     // All libFuzzer flags start with a single dash, our arguments all start
     // with a double dash. We can thus filter out the arguments meant for gflags
diff --git a/driver/jvm_tooling.cpp b/driver/jvm_tooling.cpp
index ad5e0ad..1f10994 100644
--- a/driver/jvm_tooling.cpp
+++ b/driver/jvm_tooling.cpp
@@ -128,11 +128,8 @@
 DEFINE_string(autofuzz_ignore, "",
               "Fully qualified class names of exceptions to ignore during "
               "autofuzz. Separated by comma.");
-DEFINE_bool(
-    fake_pcs, false,
-    "Supply synthetic Java program counters to libFuzzer trace hooks to "
-    "make value profiling more effective. Enabled by default if "
-    "-use_value_profile=1 is specified.");
+DEFINE_bool(fake_pcs, false,
+            "No-op flag that remains for backwards compatibility only.");
 
 #if defined(_WIN32) || defined(_WIN64)
 #define ARG_SEPARATOR ";"
@@ -230,8 +227,6 @@
       absl::StrFormat("-Djazzer.autofuzz=%s", FLAGS_autofuzz),
       absl::StrFormat("-Djazzer.autofuzz_ignore=%s", FLAGS_autofuzz_ignore),
       absl::StrFormat("-Djazzer.hooks=%s", FLAGS_hooks ? "true" : "false"),
-      absl::StrFormat("-Djazzer.fake_pcs=%s",
-                      FLAGS_fake_pcs ? "true" : "false"),
   };
 }
 
diff --git a/driver/sanitizer_hooks_with_pc.cpp b/driver/sanitizer_hooks_with_pc.cpp
deleted file mode 100644
index eb68add..0000000
--- a/driver/sanitizer_hooks_with_pc.cpp
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "sanitizer_hooks_with_pc.h"
-
-#include <cstddef>
-#include <cstdint>
-
-// libFuzzer's compare hooks obtain the caller's address from the compiler
-// builtin __builtin_return_adress. Since Java code will invoke the hooks always
-// from the same native function, this builtin would always return the same
-// value. Internally, the libFuzzer hooks call through to the always inlined
-// HandleCmp and thus can't be mimicked without patching libFuzzer.
-//
-// We solve this problem via an inline assembly trampoline construction that
-// translates a runtime argument `fake_pc` in the range [0, 512) into a call to
-// a hook with a fake return address whose lower 9 bits are `fake_pc` up to a
-// constant shift. This is achieved by pushing a return address pointing into
-// 512 ret instructions at offset `fake_pc` onto the stack and then jumping
-// directly to the address of the hook.
-//
-// Note: We only set the lowest 9 bits of the return address since only these
-// bits are used by the libFuzzer value profiling mode for integer compares, see
-// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390
-// as well as
-// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerValueBitMap.h#L34
-// ValueProfileMap.AddValue() truncates its argument to 16 bits and shifts the
-// PC to the left by log_2(128)=7, which means that only the lowest 16 - 7 bits
-// of the return address matter. String compare hooks use the lowest 12 bits,
-// but take the return address as an argument and thus don't require the
-// indirection through a trampoline.
-
-#define REPEAT_2(a) a a
-
-#define REPEAT_8(a) REPEAT_2(REPEAT_2(REPEAT_2(a)))
-
-#define REPEAT_128(a) REPEAT_2(REPEAT_8(REPEAT_8(a)))
-
-#define REPEAT_512(a) REPEAT_8(REPEAT_8(REPEAT_8(a)))
-
-// The first four registers to pass arguments in according to the
-// platform-specific x64 calling convention.
-#ifdef __aarch64__
-#define REG_1 "x0"
-#define REG_2 "x1"
-#define REG_3 "x2"
-#define REG_4 "x3"
-#elif _WIN64
-#define REG_1 "rcx"
-#define REG_2 "rdx"
-#define REG_3 "r8"
-#define REG_4 "r9"
-#else
-#define REG_1 "rdi"
-#define REG_2 "rsi"
-#define REG_3 "rdx"
-#define REG_4 "rcx"
-#endif
-
-// Call the function at address `func` with arguments `arg1` and `arg2` while
-// ensuring that the return address is `fake_pc` up to a globally constant
-// offset.
-__attribute__((noinline)) void trampoline(uint64_t arg1, uint64_t arg2,
-                                          void *func, uint16_t fake_pc) {
-  // arg1 and arg2 have to be forwarded according to the calling convention.
-  // We also fix func and fake_pc to their registers so that we can safely use
-  // rax below.
-  [[maybe_unused]] register uint64_t arg1_loc asm(REG_1) = arg1;
-  [[maybe_unused]] register uint64_t arg2_loc asm(REG_2) = arg2;
-  [[maybe_unused]] register void *func_loc asm(REG_3) = func;
-  [[maybe_unused]] register uint64_t fake_pc_loc asm(REG_4) = fake_pc;
-#ifdef __aarch64__
-  asm volatile(
-      // Load address of the ret sled into the default register for the return
-      // address (offset of four instructions, which means 16 bytes).
-      "adr x30, 16 \n\t"
-      // Clear the lowest 2 bits of fake_pc. All arm64 instructions are four
-      // bytes long, so we can't get better return address granularity than
-      // multiples of 4.
-      "and %[fake_pc], %[fake_pc], #0xFFFFFFFFFFFFFFFC \n\t"
-      // Add the offset of the fake_pc-th ret (rounded to 0 mod 4 above).
-      "add x30, x30, %[fake_pc] \n\t"
-      // Call the function by jumping to it and reusing all registers except
-      // for the modified return address register r30.
-      "br %[func] \n\t"
-      // The ret sled for arm64 consists of 128 b instructions jumping to the
-      // end of the function. Each instruction is 4 bytes long. The sled thus
-      // has the same byte length of 4 * 128 = 512 as the x86_64 sled, but
-      // coarser granularity.
-      REPEAT_128("b end_of_function\n\t") "end_of_function:\n\t"
-      :
-      : "r"(arg1_loc),
-        "r"(arg2_loc), [func] "r"(func_loc), [fake_pc] "r"(fake_pc_loc)
-      : "memory", "x30");
-#else
-  asm volatile goto(
-      // Load RIP-relative address of the end of this function.
-      "lea %l[end_of_function](%%rip), %%rax \n\t"
-      "push %%rax \n\t"
-      // Load RIP-relative address of the ret sled into rax.
-      "lea ret_sled(%%rip), %%rax \n\t"
-      // Add the offset of the fake_pc-th ret.
-      "add %[fake_pc], %%rax \n\t"
-      // Push the fake return address pointing to that ret. The hook will return
-      // to it and then immediately return to the end of this function.
-      "push %%rax \n\t"
-      // Call func with the fake return address on the stack.
-      // Function arguments arg1 and arg2 are passed unchanged in the registers
-      // RDI and RSI as governed by the x64 calling convention.
-      "jmp *%[func] \n\t"
-      // Append a sled of 2^9=512 ret instructions.
-      "ret_sled: \n\t" REPEAT_512("ret \n\t")
-      :
-      : "r"(arg1_loc),
-        "r"(arg2_loc), [func] "r"(func_loc), [fake_pc] "r"(fake_pc_loc)
-      : "memory"
-      : end_of_function);
-
-end_of_function:
-  return;
-#endif
-}
-
-namespace {
-uintptr_t trampoline_offset = 0;
-}
-
-void set_trampoline_offset() {
-  // Stores the additive inverse of the current return address modulo 0x200u in
-  // trampoline_offset.
-  trampoline_offset =
-      0x200u -
-      (reinterpret_cast<uintptr_t>(__builtin_return_address(0)) & 0x1FFu);
-}
-
-// Computes the additive shift that needs to be applied to the caller PC by
-// caller_pc_to_fake_pc to make caller PC and resulting fake return address
-// in their lowest 9 bits. This offset is constant for each binary, but may vary
-// based on code generation specifics. By calibrating the trampoline, the fuzzer
-// behavior is fully determined by the seed.
-__attribute__((constructor)) void CalibrateTrampoline() {
-  trampoline(0, 0, reinterpret_cast<void *>(&set_trampoline_offset), 0);
-}
-
-// Masks any address down to its lower 9 bits, adjusting for the trampoline
-// shift.
-__attribute__((always_inline)) inline uint16_t caller_pc_to_fake_pc(
-    const void *caller_pc) {
-  return (reinterpret_cast<uintptr_t>(caller_pc) + trampoline_offset) & 0x1FFu;
-}
-
-// The original hooks exposed by libFuzzer. All of these get the caller's
-// address via __builtin_return_address(0).
-extern "C" {
-void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2);
-void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2);
-void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases);
-void __sanitizer_cov_trace_div4(uint32_t val);
-void __sanitizer_cov_trace_div8(uint64_t val);
-void __sanitizer_cov_trace_gep(uintptr_t idx);
-void __sanitizer_cov_trace_pc_indir(uintptr_t callee);
-}
-void __sanitizer_cov_trace_cmp4_with_pc(void *caller_pc, uint32_t arg1,
-                                        uint32_t arg2) {
-  void *trace_cmp4 = reinterpret_cast<void *>(&__sanitizer_cov_trace_cmp4);
-  auto fake_pc = caller_pc_to_fake_pc(caller_pc);
-  trampoline(static_cast<uint64_t>(arg1), static_cast<uint64_t>(arg2),
-             trace_cmp4, fake_pc);
-}
-
-void __sanitizer_cov_trace_cmp8_with_pc(void *caller_pc, uint64_t arg1,
-                                        uint64_t arg2) {
-  void *trace_cmp8 = reinterpret_cast<void *>(&__sanitizer_cov_trace_cmp8);
-  auto fake_pc = caller_pc_to_fake_pc(caller_pc);
-  trampoline(static_cast<uint64_t>(arg1), static_cast<uint64_t>(arg2),
-             trace_cmp8, fake_pc);
-}
-
-void __sanitizer_cov_trace_switch_with_pc(void *caller_pc, uint64_t val,
-                                          uint64_t *cases) {
-  void *trace_switch = reinterpret_cast<void *>(&__sanitizer_cov_trace_switch);
-  auto fake_pc = caller_pc_to_fake_pc(caller_pc);
-  trampoline(static_cast<uint64_t>(val), reinterpret_cast<uint64_t>(cases),
-             trace_switch, fake_pc);
-}
-
-void __sanitizer_cov_trace_div4_with_pc(void *caller_pc, uint32_t val) {
-  void *trace_div4 = reinterpret_cast<void *>(&__sanitizer_cov_trace_div4);
-  auto fake_pc = caller_pc_to_fake_pc(caller_pc);
-  trampoline(static_cast<uint64_t>(val), 0, trace_div4, fake_pc);
-}
-
-void __sanitizer_cov_trace_div8_with_pc(void *caller_pc, uint64_t val) {
-  void *trace_div8 = reinterpret_cast<void *>(&__sanitizer_cov_trace_div8);
-  auto fake_pc = caller_pc_to_fake_pc(caller_pc);
-  trampoline(static_cast<uint64_t>(val), 0, trace_div8, fake_pc);
-}
-
-void __sanitizer_cov_trace_gep_with_pc(void *caller_pc, uintptr_t idx) {
-  void *trace_gep = reinterpret_cast<void *>(&__sanitizer_cov_trace_gep);
-  auto fake_pc = caller_pc_to_fake_pc(caller_pc);
-  trampoline(static_cast<uint64_t>(idx), 0, trace_gep, fake_pc);
-}
-
-void __sanitizer_cov_trace_pc_indir_with_pc(void *caller_pc, uintptr_t callee) {
-  void *trace_pc_indir =
-      reinterpret_cast<void *>(&__sanitizer_cov_trace_pc_indir);
-  auto fake_pc = caller_pc_to_fake_pc(caller_pc);
-  trampoline(static_cast<uint64_t>(callee), 0, trace_pc_indir, fake_pc);
-}
diff --git a/driver/sanitizer_hooks_with_pc.h b/driver/sanitizer_hooks_with_pc.h
index d986131..6034037 100644
--- a/driver/sanitizer_hooks_with_pc.h
+++ b/driver/sanitizer_hooks_with_pc.h
@@ -43,5 +43,3 @@
 
 void __sanitizer_cov_trace_pc_indir_with_pc(void *caller_pc, uintptr_t callee);
 }
-
-void CalibrateTrampoline();
diff --git a/driver/sanitizer_hooks_with_pc_test.cpp b/driver/sanitizer_hooks_with_pc_test.cpp
deleted file mode 100644
index f18ba14..0000000
--- a/driver/sanitizer_hooks_with_pc_test.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "sanitizer_hooks_with_pc.h"
-
-#include <algorithm>
-#include <cmath>
-#include <cstdint>
-#include <iostream>
-
-#include "gtest/gtest.h"
-
-static std::vector<uint16_t> gCoverageMap(512);
-
-inline void __attribute__((always_inline)) RecordCoverage() {
-  auto return_address =
-      reinterpret_cast<uintptr_t>(__builtin_return_address(0));
-  auto idx = return_address & (gCoverageMap.size() - 1);
-  gCoverageMap[idx]++;
-}
-
-extern "C" {
-void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) {
-  RecordCoverage();
-}
-
-void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {
-  RecordCoverage();
-}
-
-void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
-  RecordCoverage();
-}
-
-void __sanitizer_cov_trace_div4(uint32_t val) { RecordCoverage(); }
-
-void __sanitizer_cov_trace_div8(uint64_t val) { RecordCoverage(); }
-
-void __sanitizer_cov_trace_gep(uintptr_t idx) { RecordCoverage(); }
-
-void __sanitizer_cov_trace_pc_indir(uintptr_t callee) { RecordCoverage(); }
-}
-
-void ClearCoverage() { std::fill(gCoverageMap.begin(), gCoverageMap.end(), 0); }
-
-bool HasOptimalPcCoverage() {
-#ifdef __aarch64__
-  // All arm64 instructions are four bytes long and aligned to four bytes, so
-  // the lower two bits of each PC are fixed to 00.
-  return std::count(gCoverageMap.cbegin(), gCoverageMap.cend(), 0) <=
-         3 * gCoverageMap.size() / 4;
-#else
-  return std::count(gCoverageMap.cbegin(), gCoverageMap.cend(), 0) == 0;
-#endif
-}
-
-bool HasSingleCoveredPc() {
-  return std::count(gCoverageMap.cbegin(), gCoverageMap.cend(), 0) ==
-         gCoverageMap.size() - 1;
-}
-
-std::string PrettyPrintCoverage() {
-  std::ostringstream out;
-  std::size_t break_after = 16;
-  out << "Coverage:" << std::endl;
-  for (uintptr_t i = 0; i < gCoverageMap.size(); i++) {
-    out << (gCoverageMap[i] ? "X" : "_");
-    if (i % break_after == break_after - 1) out << std::endl;
-  }
-  return out.str();
-}
-
-class TestFakePcTrampoline : public ::testing::Test {
- protected:
-  TestFakePcTrampoline() {
-    ClearCoverage();
-    CalibrateTrampoline();
-  }
-};
-
-TEST_F(TestFakePcTrampoline, TraceCmp4Direct) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_cmp4(i, i);
-  }
-  EXPECT_TRUE(HasSingleCoveredPc()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceCmp8Direct) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_cmp8(i, i);
-  }
-  EXPECT_TRUE(HasSingleCoveredPc()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceSwitchDirect) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_switch(i, nullptr);
-  }
-  EXPECT_TRUE(HasSingleCoveredPc()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceDiv4Direct) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_div4(i);
-  }
-  EXPECT_TRUE(HasSingleCoveredPc()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceDiv8Direct) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_div8(i);
-  }
-  EXPECT_TRUE(HasSingleCoveredPc()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceGepDirect) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_gep(i);
-  }
-  EXPECT_TRUE(HasSingleCoveredPc()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TracePcIndirDirect) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_pc_indir(i);
-  }
-  EXPECT_TRUE(HasSingleCoveredPc()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceCmp4Trampoline) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_cmp4_with_pc(reinterpret_cast<void *>(i), i, i);
-  }
-  EXPECT_TRUE(HasOptimalPcCoverage()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceCmp8Trampoline) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_cmp8_with_pc(reinterpret_cast<void *>(i), i, i);
-  }
-  EXPECT_TRUE(HasOptimalPcCoverage()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceSwitchTrampoline) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_switch_with_pc(reinterpret_cast<void *>(i), i,
-                                         nullptr);
-  }
-  EXPECT_TRUE(HasOptimalPcCoverage()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceDiv4Trampoline) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_div4_with_pc(reinterpret_cast<void *>(i), i);
-  }
-  EXPECT_TRUE(HasOptimalPcCoverage()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceDiv8Trampoline) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_div8_with_pc(reinterpret_cast<void *>(i), i);
-  }
-  EXPECT_TRUE(HasOptimalPcCoverage()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TraceGepTrampoline) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_gep_with_pc(reinterpret_cast<void *>(i), i);
-  }
-  EXPECT_TRUE(HasOptimalPcCoverage()) << PrettyPrintCoverage();
-}
-
-TEST_F(TestFakePcTrampoline, TracePcIndirTrampoline) {
-  for (uint32_t i = 0; i < gCoverageMap.size(); ++i) {
-    __sanitizer_cov_trace_pc_indir_with_pc(reinterpret_cast<void *>(i), i);
-  }
-  EXPECT_TRUE(HasOptimalPcCoverage()) << PrettyPrintCoverage();
-}
diff --git a/driver/sanitizer_symbols_for_tests.cpp b/driver/sanitizer_symbols_for_tests.cpp
index 5cbd862..ab18dbf 100644
--- a/driver/sanitizer_symbols_for_tests.cpp
+++ b/driver/sanitizer_symbols_for_tests.cpp
@@ -31,13 +31,19 @@
 void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
                                   const void *s2, size_t len2, void *result) {}
 void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) {}
-void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {}
 void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {}
-void __sanitizer_cov_trace_div4(uint32_t val) {}
-void __sanitizer_cov_trace_div8(uint64_t val) {}
-void __sanitizer_cov_trace_gep(uintptr_t idx) {}
-void __sanitizer_cov_trace_pc_indir(uintptr_t callee) {}
 void __sanitizer_set_death_callback(void (*callback)()) {}
+void __sanitizer_cov_trace_cmp4_with_pc(void *caller_pc, uint32_t arg1,
+                                        uint32_t arg2) {}
+void __sanitizer_cov_trace_cmp8_with_pc(void *caller_pc, uint64_t arg1,
+                                        uint64_t arg2) {}
+void __sanitizer_cov_trace_switch_with_pc(void *caller_pc, uint64_t val,
+                                          uint64_t *cases) {}
+void __sanitizer_cov_trace_div4_with_pc(void *caller_pc, uint32_t val) {}
+void __sanitizer_cov_trace_div8_with_pc(void *caller_pc, uint64_t val) {}
+void __sanitizer_cov_trace_gep_with_pc(void *caller_pc, uintptr_t idx) {}
+void __sanitizer_cov_trace_pc_indir_with_pc(void *caller_pc, uintptr_t callee) {
+}
 int LLVMFuzzerRunDriver(int *argc, char ***argv,
                         int (*UserCb)(const uint8_t *Data, size_t Size)) {
   return 0;
diff --git a/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java
index 1e45f42..b68ef6f 100644
--- a/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java
+++ b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java
@@ -37,9 +37,9 @@
       if (plaintextBlocks.length != 2)
         return;
       if (insecureEncrypt(plaintextBlocks[0]) == 0x9fc48ee64d3dc090L) {
-        // Without --fake_pcs (enabled by default with -use_value_profile=1), the fuzzer would get
-        // stuck here as the value profile information for long comparisons would not be able to
-        // distinguish between this comparison and the one above.
+        // Without variants of the fuzzer hooks for compares that also take in fake PCs, the fuzzer
+        // would get stuck here as the value profile information for long comparisons would not be
+        // able to distinguish between this comparison and the one above.
         if (insecureEncrypt(plaintextBlocks[1]) == 0x888a82ff483ad9c2L) {
           mustNeverBeCalled();
         }