[compiler-rt] [compiler-rt][GWP-ASan] Use no-op backtrace if execinfo.h missing (PR #201266)

Brian Cain via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 27 18:42:00 PDT 2026


https://github.com/androm3da updated https://github.com/llvm/llvm-project/pull/201266

>From 02c9d4fb1485e64c916287b5e52658f6dae2c982 Mon Sep 17 00:00:00 2001
From: Brian Cain <brian.cain at oss.qualcomm.com>
Date: Tue, 2 Jun 2026 19:50:25 -0500
Subject: [PATCH 1/3] [compiler-rt][GWP-ASan] Use no-op backtrace if execinfo.h
 missing

backtrace_linux_libc.cpp requires execinfo.h, which is not present in some
C libraries (musl for example).

Add a cmake check_include_file guard: on targets with execinfo.h the existing
RTGwpAsanBacktraceLibc is built unchanged; on targets without it a new
RTGwpAsanBacktraceNone stub is built instead (all three function pointers
return nullptr).  Mirror the same check in scudo/standalone so it links
against whichever library was built.
---
 compiler-rt/lib/gwp_asan/CMakeLists.txt       | 23 +++++++++++++++----
 .../lib/gwp_asan/optional/backtrace_none.cpp  | 22 ++++++++++++++++++
 .../lib/scudo/standalone/CMakeLists.txt       | 11 ++++++++-
 3 files changed, 50 insertions(+), 6 deletions(-)
 create mode 100644 compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp

diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt
index f638adda3abe1..6a7aaf19d05a1 100644
--- a/compiler-rt/lib/gwp_asan/CMakeLists.txt
+++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt
@@ -90,11 +90,24 @@ add_compiler_rt_object_libraries(RTGwpAsanOptionsParser
     CFLAGS ${GWP_ASAN_OPTIONS_PARSER_CFLAGS})
 
 # As above, build the pre-implemented optional backtrace support libraries.
-add_compiler_rt_object_libraries(RTGwpAsanBacktraceLibc
-    ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
-    SOURCES optional/backtrace_linux_libc.cpp
-    ADDITIONAL_HEADERS ${GWP_ASAN_BACKTRACE_HEADERS}
-    CFLAGS ${GWP_ASAN_CFLAGS})
+# backtrace_linux_libc.cpp requires execinfo.h.  On targets without it
+# build RTGwpAsanBacktraceNone which provides null stubs so that
+# GWP-ASan still functions as a guard allocator without stack-trace capture.
+include(CheckIncludeFile)
+check_include_file(execinfo.h COMPILER_RT_HAS_EXECINFO_H)
+if(COMPILER_RT_HAS_EXECINFO_H)
+  add_compiler_rt_object_libraries(RTGwpAsanBacktraceLibc
+      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
+      SOURCES optional/backtrace_linux_libc.cpp
+      ADDITIONAL_HEADERS ${GWP_ASAN_BACKTRACE_HEADERS}
+      CFLAGS ${GWP_ASAN_CFLAGS})
+else()
+  add_compiler_rt_object_libraries(RTGwpAsanBacktraceNone
+      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
+      SOURCES optional/backtrace_none.cpp
+      ADDITIONAL_HEADERS ${GWP_ASAN_BACKTRACE_HEADERS}
+      CFLAGS ${GWP_ASAN_CFLAGS})
+endif()
 add_compiler_rt_object_libraries(RTGwpAsanSegvHandler
     ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
     SOURCES optional/segv_handler_posix.cpp
diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp b/compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp
new file mode 100644
index 0000000000000..0530d2bb0c993
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp
@@ -0,0 +1,22 @@
+//===-- backtrace_none.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Stub backtrace implementation for targets where execinfo.h is unavailable
+// (e.g. musl libc).  GWP-ASan still functions as a guard allocator; only
+// the stack-trace capture is absent.
+
+#include "gwp_asan/optional/backtrace.h"
+
+namespace gwp_asan {
+namespace backtrace {
+
+options::Backtrace_t getBacktraceFunction() { return nullptr; }
+PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; }
+SegvBacktrace_t getSegvBacktraceFunction() { return nullptr; }
+
+} // namespace backtrace
+} // namespace gwp_asan
diff --git a/compiler-rt/lib/scudo/standalone/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/CMakeLists.txt
index 20c59b4600613..1718f7e083f61 100644
--- a/compiler-rt/lib/scudo/standalone/CMakeLists.txt
+++ b/compiler-rt/lib/scudo/standalone/CMakeLists.txt
@@ -169,8 +169,17 @@ if (COMPILER_RT_HAS_GWP_ASAN)
   endif()
 
   add_dependencies(scudo_standalone gwp_asan)
+  # RTGwpAsanBacktraceLibc requires execinfo.h (not available on some C
+  # libs. Use RTGwpAsanBacktraceNone which provides null stubs.
+  include(CheckIncludeFile)
+  check_include_file(execinfo.h COMPILER_RT_HAS_EXECINFO_H)
+  if(COMPILER_RT_HAS_EXECINFO_H)
+    set(_gwp_asan_backtrace_lib RTGwpAsanBacktraceLibc)
+  else()
+    set(_gwp_asan_backtrace_lib RTGwpAsanBacktraceNone)
+  endif()
   list(APPEND SCUDO_OBJECT_LIBS
-       RTGwpAsan RTGwpAsanBacktraceLibc RTGwpAsanSegvHandler
+       RTGwpAsan ${_gwp_asan_backtrace_lib} RTGwpAsanSegvHandler
        RTGwpAsanOptionsParser)
 
   append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fno-omit-frame-pointer

>From 2f5b25ce94db6aeb5dba0f2e3e0d7374b117d241 Mon Sep 17 00:00:00 2001
From: Brian Cain <brian.cain at oss.qualcomm.com>
Date: Mon, 8 Jun 2026 21:19:37 -0700
Subject: [PATCH 2/3] [gwp_asan] Use llvm-libunwind for backtrace when
 COMPILER_RT_USE_LLVM_UNWINDER is set

When COMPILER_RT_USE_LLVM_UNWINDER is set, backtrace_linux_libc.cpp now
uses _Unwind_Backtrace from <unwind.h> (llvm-libunwind) instead of the
libc backtrace() from <execinfo.h>.  Both paths rely on DWARF CFI
(.eh_frame) to walk the stack; the motivation is availability, not
unwinding quality.  musl libc (used on Hexagon Linux targets) does not
provide execinfo.h / backtrace(), so calling _Unwind_Backtrace from
llvm-libunwind directly enables backtrace capture on those targets
instead of falling back to null stubs.

CMakeLists.txt:
- Check COMPILER_RT_USE_LLVM_UNWINDER before falling through to the
  execinfo.h check, so RTGwpAsanBacktraceLibc is built on targets that
  have llvm-libunwind but lack execinfo.h (e.g. musl-based systems).
- Pass -DGWP_ASAN_LIBUNWIND to the compilation when using llvm-libunwind.

backtrace_linux_libc.cpp:
- When GWP_ASAN_LIBUNWIND is defined: implement Backtrace() via
  _Unwind_Backtrace / _Unwind_GetIP; PrintBacktrace() emits raw
  addresses (suitable for offline symbolisation with llvm-symbolizer).
- When GWP_ASAN_LIBUNWIND is not defined: behaviour is unchanged -
  libc backtrace() / backtrace_symbols() are used as before.
---
 compiler-rt/lib/gwp_asan/CMakeLists.txt       | 17 ++++--
 .../optional/backtrace_linux_libc.cpp         | 57 +++++++++++++++++--
 2 files changed, 63 insertions(+), 11 deletions(-)

diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt
index 6a7aaf19d05a1..47cb97902bc48 100644
--- a/compiler-rt/lib/gwp_asan/CMakeLists.txt
+++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt
@@ -90,17 +90,24 @@ add_compiler_rt_object_libraries(RTGwpAsanOptionsParser
     CFLAGS ${GWP_ASAN_OPTIONS_PARSER_CFLAGS})
 
 # As above, build the pre-implemented optional backtrace support libraries.
-# backtrace_linux_libc.cpp requires execinfo.h.  On targets without it
-# build RTGwpAsanBacktraceNone which provides null stubs so that
-# GWP-ASan still functions as a guard allocator without stack-trace capture.
+# When COMPILER_RT_USE_LLVM_UNWINDER is set, backtrace_linux_libc.cpp uses
+# _Unwind_Backtrace from llvm-libunwind for higher-quality unwinding.
+# Otherwise it falls back to the libc backtrace() which requires execinfo.h.
+# On targets without either, build RTGwpAsanBacktraceNone which provides null
+# stubs so that GWP-ASan still functions as a guard allocator without
+# stack-trace capture.
 include(CheckIncludeFile)
 check_include_file(execinfo.h COMPILER_RT_HAS_EXECINFO_H)
-if(COMPILER_RT_HAS_EXECINFO_H)
+if(COMPILER_RT_USE_LLVM_UNWINDER OR COMPILER_RT_HAS_EXECINFO_H)
+  set(GWP_ASAN_BACKTRACE_LIBC_CFLAGS ${GWP_ASAN_CFLAGS})
+  if(COMPILER_RT_USE_LLVM_UNWINDER)
+    list(APPEND GWP_ASAN_BACKTRACE_LIBC_CFLAGS -DGWP_ASAN_LIBUNWIND)
+  endif()
   add_compiler_rt_object_libraries(RTGwpAsanBacktraceLibc
       ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
       SOURCES optional/backtrace_linux_libc.cpp
       ADDITIONAL_HEADERS ${GWP_ASAN_BACKTRACE_HEADERS}
-      CFLAGS ${GWP_ASAN_CFLAGS})
+      CFLAGS ${GWP_ASAN_BACKTRACE_LIBC_CFLAGS})
 else()
   add_compiler_rt_object_libraries(RTGwpAsanBacktraceNone
       ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp b/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp
index ea8e72be287da..fa802119fa7d0 100644
--- a/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp
+++ b/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp
@@ -6,26 +6,65 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <assert.h>
-#include <execinfo.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "gwp_asan/definitions.h"
 #include "gwp_asan/optional/backtrace.h"
 #include "gwp_asan/optional/printf.h"
 #include "gwp_asan/options.h"
 
+#ifdef GWP_ASAN_LIBUNWIND
+#include <unwind.h>
+
 namespace {
+
+struct UnwindState {
+  uintptr_t *Buffer;
+  size_t Pos;
+  size_t Size;
+};
+
+static _Unwind_Reason_Code UnwindCallback(struct _Unwind_Context *Context,
+                                          void *Arg) {
+  UnwindState *State = reinterpret_cast<UnwindState *>(Arg);
+  uintptr_t PC = (uintptr_t)_Unwind_GetIP(Context);
+  if (PC == 0)
+    return _URC_END_OF_STACK;
+  if (State->Pos < State->Size)
+    State->Buffer[State->Pos++] = PC;
+  if (State->Pos == State->Size)
+    return _URC_END_OF_STACK;
+  return _URC_NO_REASON;
+}
+
 size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
-  static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not void*");
+  UnwindState State = {TraceBuffer, 0, Size};
+  _Unwind_Backtrace(UnwindCallback, &State);
+  return State.Pos;
+}
+
+} // anonymous namespace
+
+#else // GWP_ASAN_LIBUNWIND
+
+#include <execinfo.h>
 
+namespace {
+
+size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
+  static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not void*");
   return backtrace(reinterpret_cast<void **>(TraceBuffer), Size);
 }
 
-// We don't need any custom handling for the Segv backtrace - the libc unwinder
+} // anonymous namespace
+
+#endif // GWP_ASAN_LIBUNWIND
+
+namespace {
+
+// We don't need any custom handling for the Segv backtrace - the unwinder
 // has no problems with unwinding through a signal handler. Force inlining here
 // to avoid the additional frame.
 GWP_ASAN_ALWAYS_INLINE size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size,
@@ -40,6 +79,7 @@ static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
     return;
   }
 
+#ifndef GWP_ASAN_LIBUNWIND
   char **BacktraceSymbols =
       backtrace_symbols(reinterpret_cast<void **>(Trace), TraceLength);
 
@@ -50,9 +90,14 @@ static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
       Printf("  #%zu %s\n", i, BacktraceSymbols[i]);
   }
 
-  Printf("\n");
   if (BacktraceSymbols)
     free(BacktraceSymbols);
+#else
+  for (size_t i = 0; i < TraceLength; ++i)
+    Printf("  #%zu %p\n", i, reinterpret_cast<void *>(Trace[i]));
+#endif
+
+  Printf("\n");
 }
 } // anonymous namespace
 

>From e63c85b0188b6280fc236dca1854d87848b082b9 Mon Sep 17 00:00:00 2001
From: Brian Cain <brian.cain at oss.qualcomm.com>
Date: Sat, 27 Jun 2026 13:36:59 -0700
Subject: [PATCH 3/3] fixup! [compiler-rt][GWP-ASan] Use no-op backtrace if
 execinfo.h missing

Use __has_include(<execinfo.h>) in the source file rather than
check_include_file().
---
 compiler-rt/lib/gwp_asan/CMakeLists.txt       | 32 +++++--------------
 .../optional/backtrace_linux_libc.cpp         | 22 +++++++++++--
 .../lib/gwp_asan/optional/backtrace_none.cpp  | 22 -------------
 .../lib/scudo/standalone/CMakeLists.txt       | 11 +------
 4 files changed, 28 insertions(+), 59 deletions(-)
 delete mode 100644 compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp

diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt
index 47cb97902bc48..d0de43b6e149c 100644
--- a/compiler-rt/lib/gwp_asan/CMakeLists.txt
+++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt
@@ -90,31 +90,15 @@ add_compiler_rt_object_libraries(RTGwpAsanOptionsParser
     CFLAGS ${GWP_ASAN_OPTIONS_PARSER_CFLAGS})
 
 # As above, build the pre-implemented optional backtrace support libraries.
-# When COMPILER_RT_USE_LLVM_UNWINDER is set, backtrace_linux_libc.cpp uses
-# _Unwind_Backtrace from llvm-libunwind for higher-quality unwinding.
-# Otherwise it falls back to the libc backtrace() which requires execinfo.h.
-# On targets without either, build RTGwpAsanBacktraceNone which provides null
-# stubs so that GWP-ASan still functions as a guard allocator without
-# stack-trace capture.
-include(CheckIncludeFile)
-check_include_file(execinfo.h COMPILER_RT_HAS_EXECINFO_H)
-if(COMPILER_RT_USE_LLVM_UNWINDER OR COMPILER_RT_HAS_EXECINFO_H)
-  set(GWP_ASAN_BACKTRACE_LIBC_CFLAGS ${GWP_ASAN_CFLAGS})
-  if(COMPILER_RT_USE_LLVM_UNWINDER)
-    list(APPEND GWP_ASAN_BACKTRACE_LIBC_CFLAGS -DGWP_ASAN_LIBUNWIND)
-  endif()
-  add_compiler_rt_object_libraries(RTGwpAsanBacktraceLibc
-      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
-      SOURCES optional/backtrace_linux_libc.cpp
-      ADDITIONAL_HEADERS ${GWP_ASAN_BACKTRACE_HEADERS}
-      CFLAGS ${GWP_ASAN_BACKTRACE_LIBC_CFLAGS})
-else()
-  add_compiler_rt_object_libraries(RTGwpAsanBacktraceNone
-      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
-      SOURCES optional/backtrace_none.cpp
-      ADDITIONAL_HEADERS ${GWP_ASAN_BACKTRACE_HEADERS}
-      CFLAGS ${GWP_ASAN_CFLAGS})
+set(GWP_ASAN_BACKTRACE_LIBC_CFLAGS ${GWP_ASAN_CFLAGS})
+if(COMPILER_RT_USE_LLVM_UNWINDER)
+  list(APPEND GWP_ASAN_BACKTRACE_LIBC_CFLAGS -DGWP_ASAN_LIBUNWIND)
 endif()
+add_compiler_rt_object_libraries(RTGwpAsanBacktraceLibc
+    ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
+    SOURCES optional/backtrace_linux_libc.cpp
+    ADDITIONAL_HEADERS ${GWP_ASAN_BACKTRACE_HEADERS}
+    CFLAGS ${GWP_ASAN_BACKTRACE_LIBC_CFLAGS})
 add_compiler_rt_object_libraries(RTGwpAsanSegvHandler
     ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
     SOURCES optional/segv_handler_posix.cpp
diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp b/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp
index fa802119fa7d0..333b404707fe6 100644
--- a/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp
+++ b/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp
@@ -47,8 +47,7 @@ size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
 
 } // anonymous namespace
 
-#else // GWP_ASAN_LIBUNWIND
-
+#elif __has_include(<execinfo.h>)
 #include <execinfo.h>
 
 namespace {
@@ -60,8 +59,24 @@ size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
 
 } // anonymous namespace
 
-#endif // GWP_ASAN_LIBUNWIND
+#else // No execinfo.h and no libunwind -- provide null stubs.
+
+namespace gwp_asan {
+namespace backtrace {
+
+options::Backtrace_t getBacktraceFunction() { return nullptr; }
+PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; }
+SegvBacktrace_t getSegvBacktraceFunction() { return nullptr; }
+
+} // namespace backtrace
+} // namespace gwp_asan
+
+// Early-exit: the rest of the file defines the real implementations which
+// are not needed when neither unwinder is available.
+#define GWP_ASAN_NO_BACKTRACE
+#endif
 
+#ifndef GWP_ASAN_NO_BACKTRACE
 namespace {
 
 // We don't need any custom handling for the Segv backtrace - the unwinder
@@ -110,3 +125,4 @@ SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; }
 
 } // namespace backtrace
 } // namespace gwp_asan
+#endif // GWP_ASAN_NO_BACKTRACE
diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp b/compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp
deleted file mode 100644
index 0530d2bb0c993..0000000000000
--- a/compiler-rt/lib/gwp_asan/optional/backtrace_none.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-//===-- backtrace_none.cpp --------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-// Stub backtrace implementation for targets where execinfo.h is unavailable
-// (e.g. musl libc).  GWP-ASan still functions as a guard allocator; only
-// the stack-trace capture is absent.
-
-#include "gwp_asan/optional/backtrace.h"
-
-namespace gwp_asan {
-namespace backtrace {
-
-options::Backtrace_t getBacktraceFunction() { return nullptr; }
-PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; }
-SegvBacktrace_t getSegvBacktraceFunction() { return nullptr; }
-
-} // namespace backtrace
-} // namespace gwp_asan
diff --git a/compiler-rt/lib/scudo/standalone/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/CMakeLists.txt
index 1718f7e083f61..20c59b4600613 100644
--- a/compiler-rt/lib/scudo/standalone/CMakeLists.txt
+++ b/compiler-rt/lib/scudo/standalone/CMakeLists.txt
@@ -169,17 +169,8 @@ if (COMPILER_RT_HAS_GWP_ASAN)
   endif()
 
   add_dependencies(scudo_standalone gwp_asan)
-  # RTGwpAsanBacktraceLibc requires execinfo.h (not available on some C
-  # libs. Use RTGwpAsanBacktraceNone which provides null stubs.
-  include(CheckIncludeFile)
-  check_include_file(execinfo.h COMPILER_RT_HAS_EXECINFO_H)
-  if(COMPILER_RT_HAS_EXECINFO_H)
-    set(_gwp_asan_backtrace_lib RTGwpAsanBacktraceLibc)
-  else()
-    set(_gwp_asan_backtrace_lib RTGwpAsanBacktraceNone)
-  endif()
   list(APPEND SCUDO_OBJECT_LIBS
-       RTGwpAsan ${_gwp_asan_backtrace_lib} RTGwpAsanSegvHandler
+       RTGwpAsan RTGwpAsanBacktraceLibc RTGwpAsanSegvHandler
        RTGwpAsanOptionsParser)
 
   append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fno-omit-frame-pointer



More information about the llvm-commits mailing list