mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	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
(cherry picked from commit 1b4826b9a2)
			
			
This commit is contained in:
		
							parent
							
								
									a77d641f01
								
							
						
					
					
						commit
						28f0c5cfdc
					
				| @ -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"], [ | ||||||
|  | |||||||
| @ -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, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Sebastian Pauka
						Sebastian Pauka