From 1b4826b9a2b21965e18ccbc0881eaede2a596d9f Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Fri, 25 Apr 2025 03:58:48 +1000 Subject: [PATCH] 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 Reviewed-by: Tony Hutter Signed-off-by: Sebastian Pauka Closes #17230 --- config/user-libunwind.m4 | 16 ++++++++++ lib/libspl/backtrace.c | 66 +++++++++++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/config/user-libunwind.m4 b/config/user-libunwind.m4 index 99ba3dcf4..94865b57f 100644 --- a/config/user-libunwind.m4 +++ b/config/user-libunwind.m4 @@ -34,6 +34,22 @@ AC_DEFUN([ZFS_AC_CONFIG_USER_LIBUNWIND], [ ], [ 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 + #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 ], [ AS_IF([test "x$with_libunwind" = "xyes"], [ diff --git a/lib/libspl/backtrace.c b/lib/libspl/backtrace.c index 2353f3576..c4a7006a9 100644 --- a/lib/libspl/backtrace.c +++ b/lib/libspl/backtrace.c @@ -41,9 +41,26 @@ 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) -#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 +#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 #include +#define LAST_REG_INDEX UNW_TDEP_LAST_REG +#endif + /* * 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); /* - * libunwind's list of possible registers for this architecture is an - * enum, unw_regnum_t. UNW_TDEP_LAST_REG is the highest-numbered - * register in that list, however, not all register numbers in this - * range are defined by the architecture, and not all defined registers - * will be present on every implementation of that architecture. - * Moreover, libunwind provides nice names for most, but not all - * registers, but these are hardcoded; a name being available does not - * mean that register is available. + * Iterate over all registers for the architecture. We've figured + * out the highest number above, however, not all register numbers in + * this range are defined by the architecture, and not all defined + * registers will be present on every implementation of that + * architecture. Moreover, libunwind provides nice names for most, but + * not all registers, but these are hardcoded; a name being available + * does not mean that register is available. * * 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 @@ -120,26 +136,42 @@ libspl_backtrace(int fd) * thing. */ 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 - * 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) 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 '?' - * 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); +#endif 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 - * "xx" is the two-char hex of libunwind's register - * number. + * No valid name, or likely llvm_libunwind returned + * unknown_register, so make one of the form "?xx", + * where "xx" is the two-char hex of libunwind's + * register number. */ buf[0] = '?'; n = spl_bt_u64_to_hex_str(regnum, 2,