diff --git a/include/common/compat.h b/include/common/compat.h index f46006ec9..331c32453 100644 --- a/include/common/compat.h +++ b/include/common/compat.h @@ -207,6 +207,22 @@ typedef struct { } empty_t; #define HA_HAVE_CRYPT_R #endif +/* some backtrace() implementations are broken or incomplete, in this case we + * can replace them. We must not do it all the time as some are more accurate + * than ours. + */ +#ifdef USE_BACKTRACE +#if defined(__aarch64__) +/* on aarch64 at least from gcc-4.7.4 to 7.4.1 we only get a single entry, which + * is pointless. Ours works though it misses the faulty function itself, + * probably due to an alternate stack for the signal handler which does not + * create a new frame hence doesn't store the caller's return address. + */ +#else +#define HA_HAVE_WORKING_BACKTRACE +#endif +#endif + #endif /* _COMMON_COMPAT_H */ /* diff --git a/include/common/standard.h b/include/common/standard.h index b278f088e..a78083318 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -22,6 +22,11 @@ #ifndef _COMMON_STANDARD_H #define _COMMON_STANDARD_H +#ifdef USE_BACKTRACE +#define _GNU_SOURCE +#include +#endif + #include #include #include @@ -1487,6 +1492,31 @@ void dump_hex(struct buffer *out, const char *pfx, const void *buf, int len, int int may_access(const void *ptr); void *resolve_sym_name(struct buffer *buf, const char *pfx, void *addr); +#if defined(USE_BACKTRACE) +/* Note that this may result in opening libgcc() on first call, so it may need + * to have been called once before chrooting. + */ +static forceinline int my_backtrace(void **buffer, int max) +{ +#ifdef HA_HAVE_WORKING_BACKTRACE + return backtrace(buffer, max); +#else + const struct frame { + const struct frame *next; + void *ra; + } *frame; + int count; + + frame = __builtin_frame_address(0); + for (count = 0; count < max && may_access(frame) && may_access(frame->ra);) { + buffer[count++] = frame->ra; + frame = frame->next; + } + return count; +#endif +} +#endif + /* same as realloc() except that ptr is also freed upon failure */ static inline void *my_realloc2(void *ptr, size_t size) { diff --git a/src/debug.c b/src/debug.c index a16bd4f72..1fbcde7c4 100644 --- a/src/debug.c +++ b/src/debug.c @@ -11,11 +11,6 @@ */ -#ifdef USE_BACKTRACE -#define _GNU_SOURCE -#include -#endif - #include #include #include @@ -105,7 +100,7 @@ void ha_thread_dump(struct buffer *buf, int thr, int calling_tid) void *addr; int dump = 0; - nptrs = backtrace(callers, sizeof(callers)/sizeof(*callers)); + nptrs = my_backtrace(callers, sizeof(callers)/sizeof(*callers)); /* The call backtrace_symbols_fd(callers, nptrs, STDOUT_FILENO) would produce similar output to the following: */ @@ -770,7 +765,7 @@ static int init_debug() * ready in memory for later use. */ void *callers[1]; - backtrace(callers, sizeof(callers)/sizeof(*callers)); + my_backtrace(callers, sizeof(callers)/sizeof(*callers)); #endif sa.sa_handler = NULL; sa.sa_sigaction = debug_handler;