Message ID | alpine.DEB.2.20.1803210120100.11698@digraph.polyomino.org.uk |
---|---|
State | New |
Headers | show |
Series | Use x86_64 backtrace as generic version | expand |
On 21/03/2018 09:21, Joseph Myers wrote: > No glibc configuration uses the present debug/backtrace.c, whereas > several #include the x86_64 version. The x86_64 version is > effectively a generic one (using _Unwind_Backtrace from libgcc, which > works much more reliably than the built-in functions used by > debug/backtrace.c). This patch moves it to debug/backtrace.c and > removes all the #includes of the x86_64 version from other > architectures which are no longer required. > > I do not know whether all the other architecture-specific backtrace > implementations that are based on _Unwind_Backtrace are required, or > whether, where their differences from the generic version do something > useful, suitable hooks could be added to the generic version to reduce > the duplication involved. With this change we still have i386, sparc, m68k, arm, and s390 which uses libgcc based backtrace implementation. I will check if i386, sparc, and arm can be simplified. > > Tested with build-many-glibcs.py that installed stripped shared > libraries are unchanged by this patch. > > 2018-03-20 Joseph Myers <joseph@codesourcery.com> > > * sysdeps/x86_64/backtrace.c: Move to .... > * debug/backtrace.c: ... here. > * sysdeps/aarch64/backtrace.c: Remove file. > * sysdeps/alpha/backtrace.c: Likewise. > * sysdeps/hppa/backtrace.c: Likewise. > * sysdeps/ia64/backtrace.c: Likewise. > * sysdeps/mips/backtrace.c: Likewise. > * sysdeps/nios2/backtrace.c: Likewise. > * sysdeps/riscv/backtrace.c: Likewise. > * sysdeps/sh/backtrace.c: Likewise. > * sysdeps/tile/backtrace.c: Likewise. LGTM. Reviewed-by: Adhemerval Zanella. > > diff --git a/debug/backtrace.c b/debug/backtrace.c > index 60d4a15..d423cc0 100644 > --- a/debug/backtrace.c > +++ b/debug/backtrace.c > @@ -1,7 +1,7 @@ > -/* Return backtrace of current program state. Generic version. > - Copyright (C) 1998-2018 Free Software Foundation, Inc. > +/* Return backtrace of current program state. > + Copyright (C) 2003-2018 Free Software Foundation, Inc. > This file is part of the GNU C Library. > - Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. > + Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. > > The GNU C Library is free software; you can redistribute it and/or > modify it under the terms of the GNU Lesser General Public > @@ -17,74 +17,118 @@ > License along with the GNU C Library; if not, see > <http://www.gnu.org/licenses/>. */ > > +#include <libc-lock.h> > +#include <dlfcn.h> > #include <execinfo.h> > -#include <signal.h> > -#include <frame.h> > -#include <sigcontextinfo.h> > -#include <ldsodefs.h> > - > -/* This implementation assumes a stack layout that matches the defaults > - used by gcc's `__builtin_frame_address' and `__builtin_return_address' > - (FP is the frame pointer register): > - > - +-----------------+ +-----------------+ > - FP -> | previous FP --------> | previous FP ------>... > - | | | | > - | return address | | return address | > - +-----------------+ +-----------------+ > - > - */ > - > -/* Get some notion of the current stack. Need not be exactly the top > - of the stack, just something somewhere in the current frame. */ > -#ifndef CURRENT_STACK_FRAME > -# define CURRENT_STACK_FRAME ({ char __csf; &__csf; }) > -#endif > +#include <gnu/lib-names.h> > +#include <stdlib.h> > +#include <unwind.h> > > -/* By default we assume that the stack grows downward. */ > -#ifndef INNER_THAN > -# define INNER_THAN < > -#endif > +struct trace_arg > +{ > + void **array; > + _Unwind_Word cfa; > + int cnt; > + int size; > +}; > + > +#ifdef SHARED > +static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); > +static _Unwind_Ptr (*unwind_getip) (struct _Unwind_Context *); > +static _Unwind_Word (*unwind_getcfa) (struct _Unwind_Context *); > +static void *libgcc_handle; > + > + > +/* Dummy version in case libgcc_s does not contain the real code. */ > +static _Unwind_Word > +dummy_getcfa (struct _Unwind_Context *ctx __attribute__ ((unused))) > +{ > + return 0; > +} > > -/* By default assume the `next' pointer in struct layout points to the > - next struct layout. */ > -#ifndef ADVANCE_STACK_FRAME > -# define ADVANCE_STACK_FRAME(next) ((struct layout *) (next)) > -#endif > > -/* By default, the frame pointer is just what we get from gcc. */ > -#ifndef FIRST_FRAME_POINTER > -# define FIRST_FRAME_POINTER __builtin_frame_address (0) > +static void > +init (void) > +{ > + libgcc_handle = __libc_dlopen (LIBGCC_S_SO); > + > + if (libgcc_handle == NULL) > + return; > + > + unwind_backtrace = __libc_dlsym (libgcc_handle, "_Unwind_Backtrace"); > + unwind_getip = __libc_dlsym (libgcc_handle, "_Unwind_GetIP"); > + if (unwind_getip == NULL) > + unwind_backtrace = NULL; > + unwind_getcfa = (__libc_dlsym (libgcc_handle, "_Unwind_GetCFA") > + ?: dummy_getcfa); > +} > +#else > +# define unwind_backtrace _Unwind_Backtrace > +# define unwind_getip _Unwind_GetIP > +# define unwind_getcfa _Unwind_GetCFA > #endif > > +static _Unwind_Reason_Code > +backtrace_helper (struct _Unwind_Context *ctx, void *a) > +{ > + struct trace_arg *arg = a; > + > + /* We are first called with address in the __backtrace function. > + Skip it. */ > + if (arg->cnt != -1) > + { > + arg->array[arg->cnt] = (void *) unwind_getip (ctx); > + > + /* Check whether we make any progress. */ > + _Unwind_Word cfa = unwind_getcfa (ctx); > + > + if (arg->cnt > 0 && arg->array[arg->cnt - 1] == arg->array[arg->cnt] > + && cfa == arg->cfa) > + return _URC_END_OF_STACK; > + arg->cfa = cfa; > + } > + if (++arg->cnt == arg->size) > + return _URC_END_OF_STACK; > + return _URC_NO_REASON; > +} > + > int > __backtrace (void **array, int size) > { > - struct layout *current; > - void *top_frame; > - void *top_stack; > - int cnt = 0; > + struct trace_arg arg = { .array = array, .cfa = 0, .size = size, .cnt = -1 }; > > - top_frame = FIRST_FRAME_POINTER; > - top_stack = CURRENT_STACK_FRAME; > + if (size <= 0) > + return 0; > > - /* We skip the call to this function, it makes no sense to record it. */ > - current = ((struct layout *) top_frame); > - while (cnt < size) > - { > - if ((void *) current INNER_THAN top_stack > - || !((void *) current INNER_THAN __libc_stack_end)) > - /* This means the address is out of range. Note that for the > - toplevel we see a frame pointer with value NULL which clearly is > - out of range. */ > - break; > +#ifdef SHARED > + __libc_once_define (static, once); > > - array[cnt++] = current->return_address; > + __libc_once (once, init); > + if (unwind_backtrace == NULL) > + return 0; > +#endif > > - current = ADVANCE_STACK_FRAME (current->next); > - } > + unwind_backtrace (backtrace_helper, &arg); > > - return cnt; > + /* _Unwind_Backtrace seems to put NULL address above > + _start. Fix it up here. */ > + if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL) > + --arg.cnt; > + return arg.cnt != -1 ? arg.cnt : 0; > } > weak_alias (__backtrace, backtrace) > libc_hidden_def (__backtrace) > + > + > +#ifdef SHARED > +/* Free all resources if necessary. */ > +libc_freeres_fn (free_mem) > +{ > + unwind_backtrace = NULL; > + if (libgcc_handle != NULL) > + { > + __libc_dlclose (libgcc_handle); > + libgcc_handle = NULL; > + } > +} > +#endif > diff --git a/sysdeps/aarch64/backtrace.c b/sysdeps/aarch64/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/aarch64/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/alpha/backtrace.c b/sysdeps/alpha/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/alpha/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/hppa/backtrace.c b/sysdeps/hppa/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/hppa/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/ia64/backtrace.c b/sysdeps/ia64/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/ia64/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/mips/backtrace.c b/sysdeps/mips/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/mips/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/nios2/backtrace.c b/sysdeps/nios2/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/nios2/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/riscv/backtrace.c b/sysdeps/riscv/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/riscv/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/sh/backtrace.c b/sysdeps/sh/backtrace.c > deleted file mode 100644 > index 4f3eafb..0000000 > --- a/sysdeps/sh/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include "../x86_64/backtrace.c" > diff --git a/sysdeps/tile/backtrace.c b/sysdeps/tile/backtrace.c > deleted file mode 100644 > index 27ce597..0000000 > --- a/sysdeps/tile/backtrace.c > +++ /dev/null > @@ -1 +0,0 @@ > -#include <sysdeps/x86_64/backtrace.c> > diff --git a/sysdeps/x86_64/backtrace.c b/sysdeps/x86_64/backtrace.c > deleted file mode 100644 > index d423cc0..0000000 > --- a/sysdeps/x86_64/backtrace.c > +++ /dev/null > @@ -1,134 +0,0 @@ > -/* Return backtrace of current program state. > - Copyright (C) 2003-2018 Free Software Foundation, Inc. > - This file is part of the GNU C Library. > - Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. > - > - The GNU C Library is free software; you can redistribute it and/or > - modify it under the terms of the GNU Lesser General Public > - License as published by the Free Software Foundation; either > - version 2.1 of the License, or (at your option) any later version. > - > - The GNU C Library is distributed in the hope that it will be useful, > - but WITHOUT ANY WARRANTY; without even the implied warranty of > - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > - Lesser General Public License for more details. > - > - You should have received a copy of the GNU Lesser General Public > - License along with the GNU C Library; if not, see > - <http://www.gnu.org/licenses/>. */ > - > -#include <libc-lock.h> > -#include <dlfcn.h> > -#include <execinfo.h> > -#include <gnu/lib-names.h> > -#include <stdlib.h> > -#include <unwind.h> > - > -struct trace_arg > -{ > - void **array; > - _Unwind_Word cfa; > - int cnt; > - int size; > -}; > - > -#ifdef SHARED > -static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); > -static _Unwind_Ptr (*unwind_getip) (struct _Unwind_Context *); > -static _Unwind_Word (*unwind_getcfa) (struct _Unwind_Context *); > -static void *libgcc_handle; > - > - > -/* Dummy version in case libgcc_s does not contain the real code. */ > -static _Unwind_Word > -dummy_getcfa (struct _Unwind_Context *ctx __attribute__ ((unused))) > -{ > - return 0; > -} > - > - > -static void > -init (void) > -{ > - libgcc_handle = __libc_dlopen (LIBGCC_S_SO); > - > - if (libgcc_handle == NULL) > - return; > - > - unwind_backtrace = __libc_dlsym (libgcc_handle, "_Unwind_Backtrace"); > - unwind_getip = __libc_dlsym (libgcc_handle, "_Unwind_GetIP"); > - if (unwind_getip == NULL) > - unwind_backtrace = NULL; > - unwind_getcfa = (__libc_dlsym (libgcc_handle, "_Unwind_GetCFA") > - ?: dummy_getcfa); > -} > -#else > -# define unwind_backtrace _Unwind_Backtrace > -# define unwind_getip _Unwind_GetIP > -# define unwind_getcfa _Unwind_GetCFA > -#endif > - > -static _Unwind_Reason_Code > -backtrace_helper (struct _Unwind_Context *ctx, void *a) > -{ > - struct trace_arg *arg = a; > - > - /* We are first called with address in the __backtrace function. > - Skip it. */ > - if (arg->cnt != -1) > - { > - arg->array[arg->cnt] = (void *) unwind_getip (ctx); > - > - /* Check whether we make any progress. */ > - _Unwind_Word cfa = unwind_getcfa (ctx); > - > - if (arg->cnt > 0 && arg->array[arg->cnt - 1] == arg->array[arg->cnt] > - && cfa == arg->cfa) > - return _URC_END_OF_STACK; > - arg->cfa = cfa; > - } > - if (++arg->cnt == arg->size) > - return _URC_END_OF_STACK; > - return _URC_NO_REASON; > -} > - > -int > -__backtrace (void **array, int size) > -{ > - struct trace_arg arg = { .array = array, .cfa = 0, .size = size, .cnt = -1 }; > - > - if (size <= 0) > - return 0; > - > -#ifdef SHARED > - __libc_once_define (static, once); > - > - __libc_once (once, init); > - if (unwind_backtrace == NULL) > - return 0; > -#endif > - > - unwind_backtrace (backtrace_helper, &arg); > - > - /* _Unwind_Backtrace seems to put NULL address above > - _start. Fix it up here. */ > - if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL) > - --arg.cnt; > - return arg.cnt != -1 ? arg.cnt : 0; > -} > -weak_alias (__backtrace, backtrace) > -libc_hidden_def (__backtrace) > - > - > -#ifdef SHARED > -/* Free all resources if necessary. */ > -libc_freeres_fn (free_mem) > -{ > - unwind_backtrace = NULL; > - if (libgcc_handle != NULL) > - { > - __libc_dlclose (libgcc_handle); > - libgcc_handle = NULL; > - } > -} > -#endif >
diff --git a/debug/backtrace.c b/debug/backtrace.c index 60d4a15..d423cc0 100644 --- a/debug/backtrace.c +++ b/debug/backtrace.c @@ -1,7 +1,7 @@ -/* Return backtrace of current program state. Generic version. - Copyright (C) 1998-2018 Free Software Foundation, Inc. +/* Return backtrace of current program state. + Copyright (C) 2003-2018 Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -17,74 +17,118 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ +#include <libc-lock.h> +#include <dlfcn.h> #include <execinfo.h> -#include <signal.h> -#include <frame.h> -#include <sigcontextinfo.h> -#include <ldsodefs.h> - -/* This implementation assumes a stack layout that matches the defaults - used by gcc's `__builtin_frame_address' and `__builtin_return_address' - (FP is the frame pointer register): - - +-----------------+ +-----------------+ - FP -> | previous FP --------> | previous FP ------>... - | | | | - | return address | | return address | - +-----------------+ +-----------------+ - - */ - -/* Get some notion of the current stack. Need not be exactly the top - of the stack, just something somewhere in the current frame. */ -#ifndef CURRENT_STACK_FRAME -# define CURRENT_STACK_FRAME ({ char __csf; &__csf; }) -#endif +#include <gnu/lib-names.h> +#include <stdlib.h> +#include <unwind.h> -/* By default we assume that the stack grows downward. */ -#ifndef INNER_THAN -# define INNER_THAN < -#endif +struct trace_arg +{ + void **array; + _Unwind_Word cfa; + int cnt; + int size; +}; + +#ifdef SHARED +static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); +static _Unwind_Ptr (*unwind_getip) (struct _Unwind_Context *); +static _Unwind_Word (*unwind_getcfa) (struct _Unwind_Context *); +static void *libgcc_handle; + + +/* Dummy version in case libgcc_s does not contain the real code. */ +static _Unwind_Word +dummy_getcfa (struct _Unwind_Context *ctx __attribute__ ((unused))) +{ + return 0; +} -/* By default assume the `next' pointer in struct layout points to the - next struct layout. */ -#ifndef ADVANCE_STACK_FRAME -# define ADVANCE_STACK_FRAME(next) ((struct layout *) (next)) -#endif -/* By default, the frame pointer is just what we get from gcc. */ -#ifndef FIRST_FRAME_POINTER -# define FIRST_FRAME_POINTER __builtin_frame_address (0) +static void +init (void) +{ + libgcc_handle = __libc_dlopen (LIBGCC_S_SO); + + if (libgcc_handle == NULL) + return; + + unwind_backtrace = __libc_dlsym (libgcc_handle, "_Unwind_Backtrace"); + unwind_getip = __libc_dlsym (libgcc_handle, "_Unwind_GetIP"); + if (unwind_getip == NULL) + unwind_backtrace = NULL; + unwind_getcfa = (__libc_dlsym (libgcc_handle, "_Unwind_GetCFA") + ?: dummy_getcfa); +} +#else +# define unwind_backtrace _Unwind_Backtrace +# define unwind_getip _Unwind_GetIP +# define unwind_getcfa _Unwind_GetCFA #endif +static _Unwind_Reason_Code +backtrace_helper (struct _Unwind_Context *ctx, void *a) +{ + struct trace_arg *arg = a; + + /* We are first called with address in the __backtrace function. + Skip it. */ + if (arg->cnt != -1) + { + arg->array[arg->cnt] = (void *) unwind_getip (ctx); + + /* Check whether we make any progress. */ + _Unwind_Word cfa = unwind_getcfa (ctx); + + if (arg->cnt > 0 && arg->array[arg->cnt - 1] == arg->array[arg->cnt] + && cfa == arg->cfa) + return _URC_END_OF_STACK; + arg->cfa = cfa; + } + if (++arg->cnt == arg->size) + return _URC_END_OF_STACK; + return _URC_NO_REASON; +} + int __backtrace (void **array, int size) { - struct layout *current; - void *top_frame; - void *top_stack; - int cnt = 0; + struct trace_arg arg = { .array = array, .cfa = 0, .size = size, .cnt = -1 }; - top_frame = FIRST_FRAME_POINTER; - top_stack = CURRENT_STACK_FRAME; + if (size <= 0) + return 0; - /* We skip the call to this function, it makes no sense to record it. */ - current = ((struct layout *) top_frame); - while (cnt < size) - { - if ((void *) current INNER_THAN top_stack - || !((void *) current INNER_THAN __libc_stack_end)) - /* This means the address is out of range. Note that for the - toplevel we see a frame pointer with value NULL which clearly is - out of range. */ - break; +#ifdef SHARED + __libc_once_define (static, once); - array[cnt++] = current->return_address; + __libc_once (once, init); + if (unwind_backtrace == NULL) + return 0; +#endif - current = ADVANCE_STACK_FRAME (current->next); - } + unwind_backtrace (backtrace_helper, &arg); - return cnt; + /* _Unwind_Backtrace seems to put NULL address above + _start. Fix it up here. */ + if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL) + --arg.cnt; + return arg.cnt != -1 ? arg.cnt : 0; } weak_alias (__backtrace, backtrace) libc_hidden_def (__backtrace) + + +#ifdef SHARED +/* Free all resources if necessary. */ +libc_freeres_fn (free_mem) +{ + unwind_backtrace = NULL; + if (libgcc_handle != NULL) + { + __libc_dlclose (libgcc_handle); + libgcc_handle = NULL; + } +} +#endif diff --git a/sysdeps/aarch64/backtrace.c b/sysdeps/aarch64/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/aarch64/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/alpha/backtrace.c b/sysdeps/alpha/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/alpha/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/hppa/backtrace.c b/sysdeps/hppa/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/hppa/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/ia64/backtrace.c b/sysdeps/ia64/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/ia64/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/mips/backtrace.c b/sysdeps/mips/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/mips/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/nios2/backtrace.c b/sysdeps/nios2/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/nios2/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/riscv/backtrace.c b/sysdeps/riscv/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/riscv/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/sh/backtrace.c b/sysdeps/sh/backtrace.c deleted file mode 100644 index 4f3eafb..0000000 --- a/sysdeps/sh/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include "../x86_64/backtrace.c" diff --git a/sysdeps/tile/backtrace.c b/sysdeps/tile/backtrace.c deleted file mode 100644 index 27ce597..0000000 --- a/sysdeps/tile/backtrace.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/x86_64/backtrace.c> diff --git a/sysdeps/x86_64/backtrace.c b/sysdeps/x86_64/backtrace.c deleted file mode 100644 index d423cc0..0000000 --- a/sysdeps/x86_64/backtrace.c +++ /dev/null @@ -1,134 +0,0 @@ -/* Return backtrace of current program state. - Copyright (C) 2003-2018 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <http://www.gnu.org/licenses/>. */ - -#include <libc-lock.h> -#include <dlfcn.h> -#include <execinfo.h> -#include <gnu/lib-names.h> -#include <stdlib.h> -#include <unwind.h> - -struct trace_arg -{ - void **array; - _Unwind_Word cfa; - int cnt; - int size; -}; - -#ifdef SHARED -static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); -static _Unwind_Ptr (*unwind_getip) (struct _Unwind_Context *); -static _Unwind_Word (*unwind_getcfa) (struct _Unwind_Context *); -static void *libgcc_handle; - - -/* Dummy version in case libgcc_s does not contain the real code. */ -static _Unwind_Word -dummy_getcfa (struct _Unwind_Context *ctx __attribute__ ((unused))) -{ - return 0; -} - - -static void -init (void) -{ - libgcc_handle = __libc_dlopen (LIBGCC_S_SO); - - if (libgcc_handle == NULL) - return; - - unwind_backtrace = __libc_dlsym (libgcc_handle, "_Unwind_Backtrace"); - unwind_getip = __libc_dlsym (libgcc_handle, "_Unwind_GetIP"); - if (unwind_getip == NULL) - unwind_backtrace = NULL; - unwind_getcfa = (__libc_dlsym (libgcc_handle, "_Unwind_GetCFA") - ?: dummy_getcfa); -} -#else -# define unwind_backtrace _Unwind_Backtrace -# define unwind_getip _Unwind_GetIP -# define unwind_getcfa _Unwind_GetCFA -#endif - -static _Unwind_Reason_Code -backtrace_helper (struct _Unwind_Context *ctx, void *a) -{ - struct trace_arg *arg = a; - - /* We are first called with address in the __backtrace function. - Skip it. */ - if (arg->cnt != -1) - { - arg->array[arg->cnt] = (void *) unwind_getip (ctx); - - /* Check whether we make any progress. */ - _Unwind_Word cfa = unwind_getcfa (ctx); - - if (arg->cnt > 0 && arg->array[arg->cnt - 1] == arg->array[arg->cnt] - && cfa == arg->cfa) - return _URC_END_OF_STACK; - arg->cfa = cfa; - } - if (++arg->cnt == arg->size) - return _URC_END_OF_STACK; - return _URC_NO_REASON; -} - -int -__backtrace (void **array, int size) -{ - struct trace_arg arg = { .array = array, .cfa = 0, .size = size, .cnt = -1 }; - - if (size <= 0) - return 0; - -#ifdef SHARED - __libc_once_define (static, once); - - __libc_once (once, init); - if (unwind_backtrace == NULL) - return 0; -#endif - - unwind_backtrace (backtrace_helper, &arg); - - /* _Unwind_Backtrace seems to put NULL address above - _start. Fix it up here. */ - if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL) - --arg.cnt; - return arg.cnt != -1 ? arg.cnt : 0; -} -weak_alias (__backtrace, backtrace) -libc_hidden_def (__backtrace) - - -#ifdef SHARED -/* Free all resources if necessary. */ -libc_freeres_fn (free_mem) -{ - unwind_backtrace = NULL; - if (libgcc_handle != NULL) - { - __libc_dlclose (libgcc_handle); - libgcc_handle = NULL; - } -} -#endif