diff --git a/zircon/public/gn/config/instrumentation/BUILD.gn b/zircon/public/gn/config/instrumentation/BUILD.gn
index ada811bbae9cd39c50c4169150f28b490b24c48c..9ea97513bfe4c62f736c173eb68f68dd9d8131e1 100644
--- a/zircon/public/gn/config/instrumentation/BUILD.gn
+++ b/zircon/public/gn/config/instrumentation/BUILD.gn
@@ -113,6 +113,15 @@ if (toolchain.tags + [ "sancov" ] - [ "sancov" ] != toolchain.tags) {
     cflags = compiler_flags
     ldflags = compiler_flags
   }
+
+  # This enables all the different kinds of sancov callbacks, not just
+  # the basic ones actually implemented by the canonical runtime.
+  config("sancov-full") {
+    compiler_flags = [ "-fsanitize-coverage=indirect-calls,trace-cmp,trace-div,trace-gep,trace-pc-guard,inline-8bit-counters,pc-table" ]
+    asmflags = compiler_flags
+    cflags = compiler_flags
+    ldflags = compiler_flags
+  }
 }
 
 config("profile") {
diff --git a/zircon/public/gn/config/standard.gni b/zircon/public/gn/config/standard.gni
index 5a7501902ed1be509c78196c87eda04a7a1742ad..66c24f454c35ef83e0054a897474be61c24ef1bd 100644
--- a/zircon/public/gn/config/standard.gni
+++ b/zircon/public/gn/config/standard.gni
@@ -293,6 +293,16 @@ standard_variants += [
       tags = [ "sancov" ]
     }
   },
+  {
+    variant = {
+      name = "ubsan-sancov-full"
+      bases = [ "ubsan" ]
+
+      # The "sancov" tag is important: see $zx/system/ulib/c/sanitizers.
+      configs = [ "$zx/public/gn/config/instrumentation:sancov-full" ]
+      tags = [ "sancov" ]
+    }
+  },
 ]
 
 # This list is appended to the $variants list for the build.  It provides
diff --git a/zircon/system/ulib/c/sanitizers/sancov-stubs.cpp b/zircon/system/ulib/c/sanitizers/sancov-stubs.cpp
index 3caba98517116b3463cc966e4683454b69992beb..26bbda011a4408b26fa8f4f7bc11b91eb0281c7a 100644
--- a/zircon/system/ulib/c/sanitizers/sancov-stubs.cpp
+++ b/zircon/system/ulib/c/sanitizers/sancov-stubs.cpp
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "sancov-stubs.h"
+
 // This file defines all the entry points that -fsanitize-coverage=...
 // instrumentation calls.  Unfortunately, LLVM does not publish any header
 // file declaring those signatures, though they are all given in
@@ -33,3 +35,13 @@
         __builtin_trap();
     }
 }
+
+// There are many hooks that are called a lot but can always safely
+// just do nothing.
+
+#define SANCOV_STUB(name) \
+    [[gnu::weak]] extern "C" void __sanitizer_cov_##name() {}
+
+SANCOV_NOOP_STUBS
+
+#undef SANCOV_STUB
diff --git a/zircon/system/ulib/c/sanitizers/sancov-stubs.h b/zircon/system/ulib/c/sanitizers/sancov-stubs.h
new file mode 100644
index 0000000000000000000000000000000000000000..146911ce7c8c034813852e37df0e1e16bcabe4f7
--- /dev/null
+++ b/zircon/system/ulib/c/sanitizers/sancov-stubs.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+// These macros call SANCOV_STUB(NAME) for each __sanitizer_cov_NAME symbol
+// that represents a function called by instrumented code.
+//
+// SANCOV_STUBS covers all the entry points.
+// SANCOV_NOOP_STUBS covers only the subset of SANCOV_STUBS where each
+// entry point is ordinarily a no-op that might be called harmlessly
+// by code during early startup before the proper runtime is in place.
+
+#define SANCOV_STUBS                 \
+    SANCOV_STUB(trace_pc_guard)      \
+    SANCOV_STUB(trace_pc_guard_init) \
+    SANCOV_NOOP_STUBS
+
+#define SANCOV_NOOP_STUBS            \
+    SANCOV_STUB(trace_cmp)           \
+    SANCOV_STUB(trace_cmp1)          \
+    SANCOV_STUB(trace_cmp2)          \
+    SANCOV_STUB(trace_cmp4)          \
+    SANCOV_STUB(trace_cmp8)          \
+    SANCOV_STUB(trace_const_cmp1)    \
+    SANCOV_STUB(trace_const_cmp2)    \
+    SANCOV_STUB(trace_const_cmp4)    \
+    SANCOV_STUB(trace_const_cmp8)    \
+    SANCOV_STUB(trace_switch)        \
+    SANCOV_STUB(trace_div4)          \
+    SANCOV_STUB(trace_div8)          \
+    SANCOV_STUB(trace_gep)           \
+    SANCOV_STUB(trace_pc)            \
+    SANCOV_STUB(trace_pc_indir)      \
+    SANCOV_STUB(8bit_counters_init)  \
+    SANCOV_STUB(pcs_init)
diff --git a/zircon/third_party/ulib/musl/ldso/BUILD.gn b/zircon/third_party/ulib/musl/ldso/BUILD.gn
index 493979e284092e90e7c19d2b6793dfcf1f893450..bfb0886079e8e8dfd7e3fe23a3e25c48078010e7 100644
--- a/zircon/third_party/ulib/musl/ldso/BUILD.gn
+++ b/zircon/third_party/ulib/musl/ldso/BUILD.gn
@@ -4,15 +4,19 @@
 
 source_set("ldso") {
   deps = [
+    ":dlstart",
     "$zx/system/ulib/ldmsg",
     "$zx/third_party/ulib/musl:musl_internal",
   ]
   sources = [
     "$zx/third_party/ulib/musl/arch/${toolchain.cpu}/dl-entry.S",
-    "dlstart.c",
     "dynlink-sancov.S",
     "dynlink.c",
   ]
+
+  # This is needed by dynlink-sancov.S and dynlink.c for "sancov-stubs.h".
+  include_dirs = [ "$zx/system/ulib/c/sanitizers" ]
+
   if (toolchain.libprefix != "") {
     # The libprefix always ends with a / but that's not part of the
     # "config" string in the loader-service protocol.
@@ -21,3 +25,14 @@ source_set("ldso") {
     defines = [ "DYNLINK_LDSVC_CONFIG=\"$ldsvc_config\"" ]
   }
 }
+
+source_set("dlstart") {
+  visibility = [ ":*" ]
+  deps = [
+    "$zx/third_party/ulib/musl:musl_internal",
+  ]
+  sources = [
+    "dlstart.c",
+  ]
+  configs += [ "$zx/public/gn/config:no_sanitizers" ]
+}
diff --git a/zircon/third_party/ulib/musl/ldso/dlstart.c b/zircon/third_party/ulib/musl/ldso/dlstart.c
index 0cc62080c6d9373cd0bb89156a8d6d452449f635..23e6f818fff2b944c22e053f1194075798111979 100644
--- a/zircon/third_party/ulib/musl/ldso/dlstart.c
+++ b/zircon/third_party/ulib/musl/ldso/dlstart.c
@@ -5,20 +5,7 @@
 #include <stdatomic.h>
 #include <stddef.h>
 
-#ifdef __clang__
-// TODO(mcgrathr): Really we want to compile just this file without
-// -fsanitize-coverage, but this works around the issue for now.
-__asm__(".weakref __sanitizer_cov_trace_pc_guard, _dlstart_sancov_dummy");
-__asm__(".pushsection .text._dlstart_sancov_dummy,\"ax\",%progbits\n"
-        ".local _dlstart_sancov_dummy\n"
-        ".type _dlstart_sancov_dummy,%function\n"
-        "_dlstart_sancov_dummy: ret\n"
-        ".size _dlstart_sancov_dummy, . - _dlstart_sancov_dummy\n"
-        ".popsection");
-#endif
-
-__LOCAL __NO_SAFESTACK NO_ASAN dl_start_return_t _dl_start(void* start_arg,
-                                                           void* vdso) {
+__LOCAL dl_start_return_t _dl_start(void* start_arg, void* vdso) {
     ElfW(Addr) base = (uintptr_t)__ehdr_start;
     const ElfW(Rel)* rel = NULL;
     const ElfW(Rela)* rela = NULL;
diff --git a/zircon/third_party/ulib/musl/ldso/dynlink-sancov.S b/zircon/third_party/ulib/musl/ldso/dynlink-sancov.S
index 606029d3bc2fd5c9607e64cb9054a9c0fb2314f8..a0d0d976766a68cbfa389231cce9bd1f911a0c4e 100644
--- a/zircon/third_party/ulib/musl/ldso/dynlink-sancov.S
+++ b/zircon/third_party/ulib/musl/ldso/dynlink-sancov.S
@@ -4,21 +4,27 @@
 
 #include <asm.h>
 
-// See the end of dynlink.c for what this is about.
-
-.section .text._dynlink_sancov_trace_pc_guard,"ax",%progbits
+#include "sancov-stubs.h"
 
-.weak __sanitizer_cov_trace_pc_guard
+// See the end of dynlink.c for what this is about.
 
-ENTRY(_dynlink_sancov_trace_pc_guard)
+.macro sancov_stub name
+    .pushsection .text._dynlink_sancov_\name,"ax",%progbits
+    .weak __sanitizer_cov_\name
+    ENTRY(_dynlink_sancov_\name)
 #ifdef __x86_64__
-    jmp *__sanitizer_cov_trace_pc_guard@GOTPCREL(%rip)
+        jmp *__sanitizer_cov_\name@GOTPCREL(%rip)
 #elif defined(__aarch64__)
-    adrp x16, :got:__sanitizer_cov_trace_pc_guard
-    ldr x16, [x16, #:got_lo12:__sanitizer_cov_trace_pc_guard]
-    br x16
+        adrp x16, :got:__sanitizer_cov_\name
+        ldr x16, [x16, #:got_lo12:__sanitizer_cov_\name]
+        br x16
 #else
 # error unsupported architecture
 #endif
-END(_dynlink_sancov_trace_pc_guard)
-.hidden _dynlink_sancov_trace_pc_guard
+    END(_dynlink_sancov_\name)
+    .hidden _dynlink_sancov_\name
+    .popsection
+.endm
+
+#define SANCOV_STUB(name) sancov_stub name;
+SANCOV_STUBS
diff --git a/zircon/third_party/ulib/musl/ldso/dynlink.c b/zircon/third_party/ulib/musl/ldso/dynlink.c
index bf7248076762aa63200db9bcd158598bc22f9eec..1774fe56b7c214fb695be425df8045220b7b106c 100644
--- a/zircon/third_party/ulib/musl/ldso/dynlink.c
+++ b/zircon/third_party/ulib/musl/ldso/dynlink.c
@@ -1696,8 +1696,7 @@ __NO_SAFESTACK static void update_tls_size(void) {
 static dl_start_return_t __dls3(void* start_arg);
 
 __NO_SAFESTACK NO_ASAN __attribute__((__visibility__("hidden")))
-dl_start_return_t __dls2(
-    void* start_arg, void* vdso_map) {
+dl_start_return_t __dls2(void* start_arg, void* vdso_map) {
     ldso.l_map.l_addr = (uintptr_t)__ehdr_start;
 
     Ehdr* ehdr = (void*)ldso.l_map.l_addr;
@@ -2702,26 +2701,39 @@ zx_status_t __sanitizer_change_code_protection(uintptr_t addr, size_t len,
 // directly to our definition.  The trampoline checks the 'runtime' flag to
 // distinguish calls before final relocation is complete, and only calls
 // into the sanitizer runtime once it's actually up.  Because of the
-// .weakref chicanery, _dynlink_sancov_trace_pc_guard must be in a separate
+// .weakref chicanery, the _dynlink_sancov_* symbols must be in a separate
 // assembly file.
-__asm__(".weakref __sanitizer_cov_trace_pc_guard, _dynlink_sancov_trampoline");
-__asm__(".hidden _dynlink_sancov_trace_pc_guard");
-__asm__(".pushsection .text._dynlink_sancov_trampoline,\"ax\",%progbits\n"
-        ".local _dynlink_sancov_trampoline\n"
-        ".type _dynlink_sancov_trampoline,%function\n"
-        "_dynlink_sancov_trampoline:\n"
+
+# include "sancov-stubs.h"
+
+# define SANCOV_STUB(name) SANCOV_STUB_ASM(#name)
+# define SANCOV_STUB_ASM(name) \
+    __asm__( \
+    ".weakref __sanitizer_cov_" name ", _dynlink_sancov_trampoline_" name "\n" \
+    ".hidden _dynlink_sancov_" name "\n" \
+    ".pushsection .text._dynlink_sancov_trampoline_" name ",\"ax\",%progbits\n"\
+    ".local _dynlink_sancov_trampoline_" name "\n" \
+    ".type _dynlink_sancov_trampoline_" name ",%function\n" \
+    "_dynlink_sancov_trampoline_" name ":\n" \
+     SANCOV_STUB_ASM_BODY(name) \
+    ".size _dynlink_sancov_trampoline_" name ", . - _dynlink_sancov_trampoline_" name "\n" \
+    ".popsection");
+
 # ifdef __x86_64__
-        "cmpl $0, _dynlink_runtime(%rip)\n"
-        "jne _dynlink_sancov_trace_pc_guard\n"
+#  define SANCOV_STUB_ASM_BODY(name) \
+        "cmpl $0, _dynlink_runtime(%rip)\n" \
+        "jne _dynlink_sancov_" name "\n" \
         "ret\n"
 # elif defined(__aarch64__)
-        "adrp x16, _dynlink_runtime\n"
-        "ldr w16, [x16, #:lo12:_dynlink_runtime]\n"
-        "cbnz w16, _dynlink_sancov_trace_pc_guard\n"
+#  define SANCOV_STUB_ASM_BODY(name) \
+        "adrp x16, _dynlink_runtime\n" \
+        "ldr w16, [x16, #:lo12:_dynlink_runtime]\n" \
+        "cbnz w16, _dynlink_sancov_" name "\n" \
         "ret\n"
 # else
 #  error unsupported architecture
 # endif
-        ".size _dynlink_sancov_trampoline, . - _dynlink_sancov_trampoline\n"
-        ".popsection");
+
+SANCOV_STUBS
+
 #endif