author | Alberto Bertogli
<albertito@blitiri.com.ar> 2012-09-02 22:46:49 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2012-09-04 23:48:17 UTC |
parent | ea20786fca65b3e3eb30c8116f7146f95bbae6a3 |
libfiu/backtrace.c | +64 | -2 |
libfiu/fiu.c | +1 | -1 |
libfiu/internal.h | +4 | -3 |
diff --git a/libfiu/backtrace.c b/libfiu/backtrace.c index 1ac8d39..c85da04 100644 --- a/libfiu/backtrace.c +++ b/libfiu/backtrace.c @@ -13,7 +13,6 @@ #include <sys/procfs.h> #include <link.h> -const int have_backtrace = 1; int get_backtrace(void *buffer, int size) { @@ -61,7 +60,6 @@ void *get_func_addr(const char *func_name) #include <stddef.h> /* for NULL */ -const int have_backtrace = 0; int get_backtrace(void *buffer, int size) { @@ -84,3 +82,67 @@ void *get_func_addr(const char *func_name) } #endif // DUMMY_BACKTRACE + +/* Ugly but useful conversion from function pointer to void *. + * This is not guaranteed by the standard, but has to work on all platforms + * where we support backtrace(), because that function assumes it so. */ +static void *fp_to_voidp(void (*funcp)()) +{ + unsigned char **p; + p = (unsigned char **) &funcp; + return *p; +} + +int backtrace_works(void (*caller)()) +{ + /* We remember the result so we don't have to compute it over an over + * again, we know it doesn't change. */ + static int works = -1; + + void *start = NULL; + void *end = NULL; + void *bt_buffer[100]; + void *pc; + int nptrs, i; + + /* Return the result if we know it. */ + if (works >= 0) + return works; + + nptrs = get_backtrace(bt_buffer, 100); + if (nptrs <= 0) { + works = 0; + return works; + } + + /* We will detect if it works by looking for the caller in the + * backtrace. */ + start = get_func_start(fp_to_voidp(caller)); + end = get_func_end(fp_to_voidp(caller)); + + if (start == NULL && end == NULL) { + works = 0; + return works; + } + + for (i = 0; i < nptrs; i++) { + pc = bt_buffer[i]; + + /* On some platforms, we have everything except + * get_func_end(), and that's ok. */ + if (end) { + if (pc >= start && pc <= end) { + works = 1; + return works; + } + } else { + if (get_func_start(pc) == start) { + works = 1; + return works; + } + } + } + + works = 0; + return works; +} diff --git a/libfiu/fiu.c b/libfiu/fiu.c index 01f0617..d9ea787 100644 --- a/libfiu/fiu.c +++ b/libfiu/fiu.c @@ -517,7 +517,7 @@ int fiu_enable_stack(const char *name, int failnum, void *failinfo, if (func_pos_in_stack != -1) return -1; - if (have_backtrace == 0) + if (backtrace_works((void (*)()) fiu_enable_stack) == 0) return -1; pf = insert_new_fail(name, failnum, failinfo, flags, PF_STACK); diff --git a/libfiu/internal.h b/libfiu/internal.h index f804735..6e25554 100644 --- a/libfiu/internal.h +++ b/libfiu/internal.h @@ -7,9 +7,6 @@ /* Recursion count, used both in fiu.c and fiu-rc.c */ extern __thread int rec_count; -/* Are these backtrace-related functions available, or dummies? */ -extern const int have_backtrace; - /* Gets a stack trace. The pointers are stored in the given buffer, which must * be of the given size. The number of entries is returned. * It's a wrapper around glibc's backtrace(). */ @@ -25,5 +22,9 @@ void *get_func_start(void *func); /* Returns a pointer to the function given by name. */ void *get_func_addr(const char *func_name); +/* Do the above backtrace-related functions work? + * Takes a pointer to the caller so it can verify it's on the stack. */ +int backtrace_works(void (*caller)()); + #endif