Support using llvm-libunwind

This commit adds support for using llvm-libunwind for kernels built
using llvm and clang. The two differences are that the largest register
index is given by _LIBUNWIND_HIGHEST_DWARF_REGISTER, we need to check
whether the register is a floating point register and the prototype
for unw_regname takes the unwind cursor as the first argument.

Reviewed-by: Rob Norris <robn@despairlabs.com>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Signed-off-by: Sebastian Pauka <me@spauka.se>
Closes #17230
This commit is contained in:
Sebastian Pauka 2025-04-25 03:58:48 +10:00 committed by GitHub
parent 7031a48c70
commit 1b4826b9a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 17 deletions

View File

@ -34,6 +34,22 @@ AC_DEFUN([ZFS_AC_CONFIG_USER_LIBUNWIND], [
], [ ], [
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
]) ])
dnl LLVM includes it's own libunwind library, which
dnl defines the highest numbered register in a different
dnl way, and has an incompatible unw_resname function.
AC_MSG_CHECKING([whether libunwind is llvm libunwind])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([
#include <libunwind.h>
#if !defined(_LIBUNWIND_HIGHEST_DWARF_REGISTER)
#error "_LIBUNWIND_HIGHEST_DWARF_REGISTER is not defined"
#endif
], [])], [
AC_MSG_RESULT([yes])
AC_DEFINE(IS_LIBUNWIND_LLVM, 1, [libunwind is llvm libunwind])
], [
AC_MSG_RESULT([no])
])
AX_RESTORE_FLAGS AX_RESTORE_FLAGS
], [ ], [
AS_IF([test "x$with_libunwind" = "xyes"], [ AS_IF([test "x$with_libunwind" = "xyes"], [

View File

@ -41,9 +41,26 @@
do { ssize_t r __maybe_unused = write(fd, s, n); } while (0) do { ssize_t r __maybe_unused = write(fd, s, n); } while (0)
#define spl_bt_write(fd, s) spl_bt_write_n(fd, s, sizeof (s)-1) #define spl_bt_write(fd, s) spl_bt_write_n(fd, s, sizeof (s)-1)
#if defined(HAVE_LIBUNWIND) #ifdef HAVE_LIBUNWIND
/*
* libunwind-gcc and libunwind-llvm both list registers using an enum,
* unw_regnum_t, however they indicate the highest numbered register for
* a given architecture in different ways. We can check which one is defined
* and mark which libunwind is in use
*/
#ifdef IS_LIBUNWIND_LLVM
#include <libunwind.h>
#define LAST_REG_INDEX _LIBUNWIND_HIGHEST_DWARF_REGISTER
#else
/*
* Need to define UNW_LOCAL_ONLY before importing libunwind.h
* if using libgcc libunwind.
*/
#define UNW_LOCAL_ONLY #define UNW_LOCAL_ONLY
#include <libunwind.h> #include <libunwind.h>
#define LAST_REG_INDEX UNW_TDEP_LAST_REG
#endif
/* /*
* Convert `v` to ASCII hex characters. The bottom `n` nybbles (4-bits ie one * Convert `v` to ASCII hex characters. The bottom `n` nybbles (4-bits ie one
@ -102,14 +119,13 @@ libspl_backtrace(int fd)
unw_init_local(&cp, &uc); unw_init_local(&cp, &uc);
/* /*
* libunwind's list of possible registers for this architecture is an * Iterate over all registers for the architecture. We've figured
* enum, unw_regnum_t. UNW_TDEP_LAST_REG is the highest-numbered * out the highest number above, however, not all register numbers in
* register in that list, however, not all register numbers in this * this range are defined by the architecture, and not all defined
* range are defined by the architecture, and not all defined registers * registers will be present on every implementation of that
* will be present on every implementation of that architecture. * architecture. Moreover, libunwind provides nice names for most, but
* Moreover, libunwind provides nice names for most, but not all * not all registers, but these are hardcoded; a name being available
* registers, but these are hardcoded; a name being available does not * does not mean that register is available.
* mean that register is available.
* *
* So, we have to pull this all together here. We try to get the value * So, we have to pull this all together here. We try to get the value
* of every possible register. If we get a value for it, then the * of every possible register. If we get a value for it, then the
@ -120,26 +136,42 @@ libspl_backtrace(int fd)
* thing. * thing.
*/ */
uint_t cols = 0; uint_t cols = 0;
for (uint_t regnum = 0; regnum <= UNW_TDEP_LAST_REG; regnum++) { for (uint_t regnum = 0; regnum <= LAST_REG_INDEX; regnum++) {
/* /*
* Get the value. Any error probably means the register * Get the value. Any error probably means the register
* doesn't exist, and we skip it. * doesn't exist, and we skip it. LLVM libunwind iterates over
* fp registers in the same list, however they have to be
* accessed using unw_get_fpreg instead. Here, we just ignore
* them.
*/ */
#ifdef IS_LIBUNWIND_LLVM
if (unw_is_fpreg(&cp, regnum) ||
unw_get_reg(&cp, regnum, &v) < 0)
continue;
#else
if (unw_get_reg(&cp, regnum, &v) < 0) if (unw_get_reg(&cp, regnum, &v) < 0)
continue; continue;
#endif
/* /*
* Register name. If libunwind doesn't have a name for it, * Register name. If GCC libunwind doesn't have a name for it,
* it will return "???". As a shortcut, we just treat '?' * it will return "???". As a shortcut, we just treat '?'
* is an alternate end-of-string character. * is an alternate end-of-string character. LLVM libunwind will
* return the string 'unknown register', which we detect by
* checking if the register name is longer than 5 characters.
*/ */
#ifdef IS_LIBUNWIND_LLVM
const char *name = unw_regname(&cp, regnum);
#else
const char *name = unw_regname(regnum); const char *name = unw_regname(regnum);
#endif
for (n = 0; name[n] != '\0' && name[n] != '?'; n++) {} for (n = 0; name[n] != '\0' && name[n] != '?'; n++) {}
if (n == 0) { if (n == 0 || n > 5) {
/* /*
* No valid name, so make one of the form "?xx", where * No valid name, or likely llvm_libunwind returned
* "xx" is the two-char hex of libunwind's register * unknown_register, so make one of the form "?xx",
* number. * where "xx" is the two-char hex of libunwind's
* register number.
*/ */
buf[0] = '?'; buf[0] = '?';
n = spl_bt_u64_to_hex_str(regnum, 2, n = spl_bt_u64_to_hex_str(regnum, 2,